mirror of
https://github.com/MinecraftForge/MinecraftForge.git
synced 2026-11-14 01:04:24 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2ce54f1855 | ||
|
|
8897351a80 | ||
|
|
b0b34c638e | ||
|
|
212da42d6e | ||
|
|
fe616ffe20 | ||
|
|
dba4691cd3 | ||
|
|
00fc008f7f | ||
|
|
e23fc25a6c | ||
|
|
b8bbf950a9 | ||
|
|
208adc1109 | ||
|
|
60904744b9 | ||
|
|
ac5141c97c | ||
|
|
0b1eefc6d5 | ||
|
|
3de4e38d10 | ||
|
|
384fe451fc | ||
|
|
6a78310a52 | ||
|
|
4305f6de49 | ||
|
|
9d64011438 | ||
|
|
a0a71c6c53 | ||
|
|
7060036589 | ||
|
|
97e6b96cbb | ||
|
|
915d8a8f7d | ||
|
|
69037051ab | ||
|
|
1fe85f5396 | ||
|
|
5693e5d11a | ||
|
|
da2f435810 | ||
|
|
c24aff8a53 | ||
|
|
d38ce3d794 | ||
|
|
87b4f6367a | ||
|
|
b81e7dfe73 | ||
|
|
9d02da81cf | ||
|
|
5b1ab8b40b | ||
|
|
542db422ad | ||
|
|
ac10315810 | ||
|
|
8006e4ff48 | ||
|
|
dd8e7ffd16 | ||
|
|
976d710c10 | ||
|
|
1b44cda8ed | ||
|
|
10bc7b8e2e | ||
|
|
333a8e16ad | ||
|
|
0554de0251 | ||
|
|
845f315c47 | ||
|
|
c6f3726b6d | ||
|
|
46ea9fca8e | ||
|
|
0f2dac73ea | ||
|
|
576c61cf7d | ||
|
|
5eb5c34636 | ||
|
|
8f17370cb7 | ||
|
|
bc0c89ff69 |
No files matched your search
@@ -9,5 +9,5 @@ dependencies {
|
||||
implementation 'net.minecraftforge:srgutils:0.5.10'
|
||||
implementation 'commons-io:commons-io:2.13.0'
|
||||
implementation 'com.google.code.gson:gson:2.10.1'
|
||||
implementation 'org.eclipse.jgit:org.eclipse.jgit:6.7.0.202309050840-r'
|
||||
implementation 'org.eclipse.jgit:org.eclipse.jgit:6.5.0.202303070854-r'
|
||||
}
|
||||
+33
-4
@@ -194,7 +194,6 @@ patcher {
|
||||
|
||||
forge_server_gametest {
|
||||
args '--launchTarget', 'forge_dev_server_gametest'
|
||||
args '--uniqueWorld' // Unique world is used so that the world regenerates, as well as the world config isn't influenced by other runs
|
||||
}
|
||||
|
||||
forge_server_gametest_test {
|
||||
@@ -405,6 +404,16 @@ tasks.register("downloadServerShim", DownloadMavenArtifact) {
|
||||
artifact = libs.bootstrap.shim.get().toString()
|
||||
changing = true
|
||||
}
|
||||
tasks.register("downloadMCPConfig", DownloadMavenArtifact) {
|
||||
artifact = MCP_ARTIFACT
|
||||
}
|
||||
tasks.register('extractMCPConfigMappings', net.minecraftforge.gradle.common.tasks.JarExec) {
|
||||
ext.output = file('build/mcp_mappings.tsrg')
|
||||
tool = INSTALLER_TOOLS + ':fatjar'
|
||||
args = ['--task', 'MCP_DATA', '--key', 'mappings', '--input', downloadMCPConfig.output.get().asFile.absolutePath, '--output', output.absolutePath]
|
||||
outputs.file(output)
|
||||
inputs.file(downloadMCPConfig.output)
|
||||
}
|
||||
tasks.register('downloadClientMappings', net.minecraftforge.gradle.common.tasks.JarExec) {
|
||||
ext.output = file('build/client_mappings.tsrg')
|
||||
tool = INSTALLER_TOOLS + ':fatjar'
|
||||
@@ -611,6 +620,9 @@ tasks.register('installerJson', InstallerJson) {
|
||||
dependsOn(launcherJson)
|
||||
input.from(launcherJson.output)
|
||||
|
||||
// Extract MCP Mappings
|
||||
dependsOn(extractMCPConfigMappings)
|
||||
input.from(extractMCPConfigMappings.output)
|
||||
// Download Official Mappings
|
||||
dependsOn(downloadClientMappings, downloadServerMappings)
|
||||
input.from(downloadClientMappings.output, downloadServerMappings.output)
|
||||
@@ -637,6 +649,14 @@ tasks.register('installerJson', InstallerJson) {
|
||||
minecraft: MC_VERSION,
|
||||
serverJarPath: '{LIBRARY_DIR}/net/minecraft/server/{MINECRAFT_VERSION}/server-{MINECRAFT_VERSION}-bundled.jar',
|
||||
data: [
|
||||
MAPPINGS: [
|
||||
client: "[${MCP_ARTIFACT.group}:${MCP_ARTIFACT.name}:${MCP_ARTIFACT.version}:mappings@tsrg]",
|
||||
server: "[${MCP_ARTIFACT.group}:${MCP_ARTIFACT.name}:${MCP_ARTIFACT.version}:mappings@tsrg]"
|
||||
],
|
||||
MAPPINGS_SHA: [
|
||||
client: "'${extractMCPConfigMappings.output.sha1}'",
|
||||
server: "'${extractMCPConfigMappings.output.sha1}'"
|
||||
],
|
||||
MOJMAPS: [
|
||||
client: "[net.minecraft:client:${MC_VERSION}:mappings@tsrg]",
|
||||
server: "[net.minecraft:server:${MC_VERSION}:mappings@tsrg]"
|
||||
@@ -726,6 +746,18 @@ tasks.register('installerJson', InstallerJson) {
|
||||
outputs: [
|
||||
'{MC_UNPACKED}': '{MC_UNPACKED_SHA}'
|
||||
]
|
||||
], [
|
||||
jar: INSTALLER_TOOLS,
|
||||
classpath: INSTALLER_TOOLS_CLASSPATH,
|
||||
args: [
|
||||
'--task', 'MCP_DATA',
|
||||
'--input', "[${MCP_ARTIFACT.descriptor}]",
|
||||
'--output', '{MAPPINGS}',
|
||||
'--key', 'mappings'
|
||||
],
|
||||
outputs: [
|
||||
'{MAPPINGS}': '{MAPPINGS_SHA}'
|
||||
]
|
||||
], [
|
||||
jar: INSTALLER_TOOLS,
|
||||
classpath: INSTALLER_TOOLS_CLASSPATH,
|
||||
@@ -816,9 +848,6 @@ tasks.named('universalJar').configure {
|
||||
exclude '.cache'
|
||||
|
||||
manifest {
|
||||
attributes([
|
||||
'Automatic-Module-Name': 'net.minecraftforge.forge'
|
||||
] as LinkedHashMap)
|
||||
attributes([
|
||||
'Specification-Title': 'Forge',
|
||||
'Specification-Vendor': 'Forge Development LLC',
|
||||
|
||||
@@ -44,7 +44,6 @@ tasks.withType(JavaCompile).configureEach {
|
||||
dependsOn 'processResources' // Needed because we merge the output of this with the output of the compile task. And gradle detects downstream tasks using the output without a hard dep
|
||||
options.encoding = 'UTF-8' // Use the UTF-8 charset for Java compilation
|
||||
options.warnings = false // Shutup deprecated for removal warnings
|
||||
options.forkOptions.jvmArgs += '-Xmx3G' // Needed to make compiling faster, and not run out of heap space in some cases.
|
||||
}
|
||||
|
||||
// Merge the resources and classes into the same directory. We'll need to split them at runtime because
|
||||
|
||||
+2
-2
@@ -2,8 +2,8 @@
|
||||
|
||||
MinecraftForge
|
||||
=============
|
||||
[][Download]
|
||||
[][Download]
|
||||
[][Download] [][Discord] [][Patreon]
|
||||
|
||||
Forge is a free, open-source modding API all of your favourite mods use!
|
||||
|
||||
@@ -57,9 +57,7 @@ public class DeferredWorkQueue
|
||||
LOGGER.debug(LOADING, "Dispatching synchronous work for work queue {}: {} jobs", modLoadingStage, tasks.size());
|
||||
RuntimeException aggregate = new RuntimeException();
|
||||
Stopwatch timer = Stopwatch.createStarted();
|
||||
for (TaskInfo t : tasks) {
|
||||
makeRunnable(t, Runnable::run, aggregate);
|
||||
}
|
||||
tasks.forEach(t -> makeRunnable(t, Runnable::run, aggregate));
|
||||
timer.stop();
|
||||
if (aggregate.getSuppressed().length > 0) {
|
||||
LOGGER.fatal(
|
||||
@@ -116,7 +114,7 @@ public class DeferredWorkQueue
|
||||
return future;
|
||||
}
|
||||
|
||||
private static final class TaskInfo {
|
||||
private static class TaskInfo {
|
||||
private final ModContainer owner;
|
||||
private Runnable task;
|
||||
private CompletableFuture<?> future;
|
||||
|
||||
@@ -61,7 +61,7 @@ public class InterModComms
|
||||
}
|
||||
}
|
||||
|
||||
private static final ConcurrentMap<String, ConcurrentLinkedQueue<IMCMessage>> containerQueues = new ConcurrentHashMap<>();
|
||||
private static ConcurrentMap<String, ConcurrentLinkedQueue<IMCMessage>> containerQueues = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* Send IMC to remote. Sender will default to the active modcontainer, or minecraft if not.
|
||||
@@ -115,14 +115,16 @@ public class InterModComms
|
||||
return getMessages(modId, s->Boolean.TRUE);
|
||||
}
|
||||
|
||||
private record QueueFilteringSpliterator(
|
||||
ConcurrentLinkedQueue<IMCMessage> queue,
|
||||
Predicate<String> methodFilter,
|
||||
Iterator<IMCMessage> iterator
|
||||
) implements Spliterator<IMCMessage> {
|
||||
private static class QueueFilteringSpliterator implements Spliterator<IMCMessage>
|
||||
{
|
||||
private final ConcurrentLinkedQueue<IMCMessage> queue;
|
||||
private final Predicate<String> methodFilter;
|
||||
private final Iterator<IMCMessage> iterator;
|
||||
|
||||
public QueueFilteringSpliterator(final ConcurrentLinkedQueue<IMCMessage> queue, final Predicate<String> methodFilter) {
|
||||
this(queue, methodFilter, queue.iterator());
|
||||
this.queue = queue;
|
||||
this.iterator = queue.iterator();
|
||||
this.methodFilter = methodFilter;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -45,7 +45,7 @@ public abstract class ModContainer
|
||||
protected final IModInfo modInfo;
|
||||
protected ModLoadingStage modLoadingStage;
|
||||
protected Supplier<?> contextExtension;
|
||||
protected final Map<ModLoadingStage, Runnable> activityMap = new EnumMap<>(ModLoadingStage.class);
|
||||
protected final Map<ModLoadingStage, Runnable> activityMap = new HashMap<>();
|
||||
protected final Map<Class<? extends IExtensionPoint<?>>, Supplier<?>> extensionPoints = new IdentityHashMap<>();
|
||||
protected final EnumMap<ModConfig.Type, ModConfig> configs = new EnumMap<>(ModConfig.Type.class);
|
||||
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
|
||||
@@ -168,10 +168,7 @@ public abstract class ModContainer
|
||||
}
|
||||
|
||||
public void dispatchConfigEvent(IConfigEvent event) {
|
||||
var handler = configHandler.orElse(null);
|
||||
if (handler != null) {
|
||||
handler.accept(event);
|
||||
}
|
||||
configHandler.ifPresent(configHandler->configHandler.accept(event));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -74,12 +74,11 @@ public class ModList
|
||||
}
|
||||
|
||||
private String fileToLine(IModFile mf) {
|
||||
var mainMod = mf.getModInfos().getFirst();
|
||||
return String.format(Locale.ENGLISH, "%-50.50s|%-30.30s|%-30.30s|%-20.20s|%-10.10s|Manifest: %s", mf.getFileName(),
|
||||
mainMod.getDisplayName(),
|
||||
mainMod.getModId(),
|
||||
mainMod.getVersion(),
|
||||
getModContainerState(mainMod.getModId()),
|
||||
mf.getModInfos().get(0).getDisplayName(),
|
||||
mf.getModInfos().get(0).getModId(),
|
||||
mf.getModInfos().get(0).getVersion(),
|
||||
getModContainerState(mf.getModInfos().get(0).getModId()),
|
||||
((ModFileInfo)mf.getModFileInfo()).getCodeSigningFingerprint().orElse("NOSIGNATURE"));
|
||||
}
|
||||
private String crashReport() {
|
||||
@@ -96,6 +95,14 @@ public class ModList
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
private static ForkJoinWorkerThread newForkJoinWorkerThread(ForkJoinPool pool) {
|
||||
ForkJoinWorkerThread thread = ForkJoinPool.defaultForkJoinWorkerThreadFactory.newThread(pool);
|
||||
thread.setName("modloading-worker-" + thread.getPoolIndex());
|
||||
// The default sets it to the SystemClassloader, so copy the current one.
|
||||
thread.setContextClassLoader(Thread.currentThread().getContextClassLoader());
|
||||
return thread;
|
||||
}
|
||||
|
||||
public List<IModFileInfo> getModFiles()
|
||||
{
|
||||
return modFiles;
|
||||
@@ -124,18 +131,16 @@ public class ModList
|
||||
CompletableFuture<Void> cf = new CompletableFuture<>();
|
||||
final RuntimeException accumulator = new RuntimeException();
|
||||
cf.completeExceptionally(accumulator);
|
||||
for (Throwable exception : throwables) {
|
||||
throwables.forEach(exception -> {
|
||||
if (exception instanceof CompletionException) {
|
||||
exception = exception.getCause();
|
||||
}
|
||||
if (exception.getSuppressed().length != 0) {
|
||||
for (Throwable throwable : exception.getSuppressed()) {
|
||||
accumulator.addSuppressed(throwable);
|
||||
}
|
||||
if (exception.getSuppressed().length!=0) {
|
||||
Arrays.stream(exception.getSuppressed()).forEach(accumulator::addSuppressed);
|
||||
} else {
|
||||
accumulator.addSuppressed(exception);
|
||||
}
|
||||
}
|
||||
});
|
||||
return cf;
|
||||
}
|
||||
}
|
||||
@@ -143,11 +148,11 @@ public class ModList
|
||||
static <V> CompletableFuture<List<Map.Entry<V, Throwable>>> gather(List<? extends CompletableFuture<? extends V>> futures) {
|
||||
List<Map.Entry<V, Throwable>> list = new ArrayList<>(futures.size());
|
||||
CompletableFuture<?>[] results = new CompletableFuture[futures.size()];
|
||||
for (var future : futures) {
|
||||
futures.forEach(future -> {
|
||||
int i = list.size();
|
||||
list.add(null);
|
||||
results[i] = future.whenComplete((result, exception) -> list.set(i, new AbstractMap.SimpleImmutableEntry<>(result, exception)));
|
||||
}
|
||||
});
|
||||
return CompletableFuture.allOf(results).handle((r, th)->null).thenApply(res -> list);
|
||||
}
|
||||
|
||||
|
||||
@@ -77,13 +77,13 @@ import static net.minecraftforge.fml.Logging.LOADING;
|
||||
public class ModLoader
|
||||
{
|
||||
private static final Logger LOGGER = LogManager.getLogger();
|
||||
private static ModLoader INSTANCE;
|
||||
private final LoadingModList loadingModList;
|
||||
|
||||
private final List<ModLoadingException> loadingExceptions;
|
||||
private final Set<IModInfo> erroredModInfos;
|
||||
private final List<ModLoadingWarning> loadingWarnings;
|
||||
private final ModStateManager stateManager;
|
||||
private static boolean loadingStateValid;
|
||||
private boolean loadingStateValid;
|
||||
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
|
||||
private final Optional<Consumer<String>> statusConsumer = StartupNotificationManager.modLoaderConsumer();
|
||||
private final Set<IModLoadingState> completedStates = new HashSet<>();
|
||||
@@ -91,6 +91,7 @@ public class ModLoader
|
||||
|
||||
private ModLoader()
|
||||
{
|
||||
INSTANCE = this;
|
||||
this.loadingModList = FMLLoader.getLoadingModList();
|
||||
this.loadingExceptions = this.loadingModList.getErrors().stream()
|
||||
.flatMap(ModLoadingException::fromEarlyException)
|
||||
@@ -98,32 +99,24 @@ public class ModLoader
|
||||
this.loadingWarnings = this.loadingModList.getBrokenFiles().stream()
|
||||
.map(file -> new ModLoadingWarning(null, ModLoadingStage.VALIDATE, InvalidModIdentifier.identifyJarProblem(file.getFilePath()).orElse("fml.modloading.brokenfile"), file.getFileName()))
|
||||
.collect(Collectors.toList());
|
||||
if (this.loadingExceptions.isEmpty()) {
|
||||
this.erroredModInfos = Collections.emptySet();
|
||||
} else {
|
||||
this.erroredModInfos = Collections.newSetFromMap(new IdentityHashMap<>());
|
||||
this.erroredModInfos.addAll(this.loadingExceptions.stream().map(ModLoadingException::getModInfo).toList());
|
||||
}
|
||||
this.loadingModList.getModFiles().stream()
|
||||
.filter(ModFileInfo::missingLicense)
|
||||
.filter(modFileInfo -> modFileInfo.getMods().stream().noneMatch(this.erroredModInfos::contains)) //Ignore files where any other mod already encountered an error
|
||||
.filter(modFileInfo -> modFileInfo.getMods().stream().noneMatch(thisModInfo -> this.loadingExceptions.stream().map(ModLoadingException::getModInfo).anyMatch(otherInfo -> otherInfo == thisModInfo))) //Ignore files where any other mod already encountered an error
|
||||
.map(modFileInfo -> new ModLoadingException(null, ModLoadingStage.VALIDATE, "fml.modloading.missinglicense", null, modFileInfo.getFile()))
|
||||
.forEach(this.loadingExceptions::add);
|
||||
this.stateManager = new ModStateManager();
|
||||
CrashReportCallables.registerCrashCallable("ModLauncher", FMLLoader::getLauncherInfo);
|
||||
CrashReportCallables.registerCrashCallable("ModLauncher launch target", FMLLoader::launcherHandlerName);
|
||||
CrashReportCallables.registerCrashCallable("ModLauncher naming", FMLLoader::getNaming);
|
||||
CrashReportCallables.registerCrashCallable("ModLauncher services", ModLoader::computeModLauncherServiceList);
|
||||
CrashReportCallables.registerCrashCallable("FML Language Providers", ModLoader::computeLanguageList);
|
||||
CrashReportCallables.registerCrashCallable("ModLauncher services", this::computeModLauncherServiceList);
|
||||
CrashReportCallables.registerCrashCallable("FML Language Providers", this::computeLanguageList);
|
||||
}
|
||||
|
||||
private static String computeLanguageList() {
|
||||
return "\n" + FMLLoader.getLanguageLoadingProvider()
|
||||
.applyForEach(lp -> lp.name() + "@" + lp.getClass().getPackage().getImplementationVersion())
|
||||
.collect(Collectors.joining("\n\t\t", "\t\t", ""));
|
||||
private String computeLanguageList() {
|
||||
return "\n"+FMLLoader.getLanguageLoadingProvider().applyForEach(lp->lp.name() +"@"+ lp.getClass().getPackage().getImplementationVersion()).collect(Collectors.joining("\n\t\t", "\t\t", ""));
|
||||
}
|
||||
|
||||
private static String computeModLauncherServiceList() {
|
||||
private String computeModLauncherServiceList() {
|
||||
final List<Map<String, String>> mods = FMLLoader.modLauncherModList();
|
||||
return "\n"+mods.stream().map(mod->mod.getOrDefault("file","nofile")+
|
||||
" "+mod.getOrDefault("name", "missing")+
|
||||
@@ -132,13 +125,9 @@ public class ModLoader
|
||||
collect(Collectors.joining("\n\t\t","\t\t",""));
|
||||
}
|
||||
|
||||
public static ModLoader get() {
|
||||
return LazyInit.INSTANCE;
|
||||
}
|
||||
|
||||
private static final class LazyInit {
|
||||
private static final ModLoader INSTANCE = new ModLoader();
|
||||
private LazyInit() {}
|
||||
public static ModLoader get()
|
||||
{
|
||||
return INSTANCE == null ? INSTANCE = new ModLoader() : INSTANCE;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -155,7 +144,7 @@ public class ModLoader
|
||||
final ModList modList = ModList.of(loadingModList.getModFiles().stream().map(ModFileInfo::getFile).toList(),
|
||||
loadingModList.getMods());
|
||||
if (!this.loadingExceptions.isEmpty()) {
|
||||
LOGGER.fatal(CORE, "Error during pre-loading phase", loadingExceptions.getFirst());
|
||||
LOGGER.fatal(CORE, "Error during pre-loading phase", loadingExceptions.get(0));
|
||||
statusConsumer.ifPresent(c->c.accept("ERROR DURING MOD LOADING"));
|
||||
modList.setLoadedMods(Collections.emptyList());
|
||||
loadingStateValid = false;
|
||||
@@ -163,7 +152,7 @@ public class ModLoader
|
||||
}
|
||||
List<? extends ForgeFeature.Bound> failedBounds = loadingModList.getMods().stream()
|
||||
.map(ModInfo::getForgeFeatures)
|
||||
.flatMap(List::stream)
|
||||
.flatMap(Collection::stream)
|
||||
.filter(bound -> !ForgeFeature.testFeature(FMLEnvironment.dist, bound))
|
||||
.toList();
|
||||
|
||||
@@ -180,10 +169,10 @@ public class ModLoader
|
||||
final List<ModContainer> modContainers = loadingModList.getModFiles().stream()
|
||||
.map(ModFileInfo::getFile)
|
||||
.map(this::buildMods)
|
||||
.flatMap(List::stream)
|
||||
.<ModContainer>mapMulti(Iterable::forEach)
|
||||
.toList();
|
||||
if (!loadingExceptions.isEmpty()) {
|
||||
LOGGER.fatal(CORE, "Failed to initialize mod containers", loadingExceptions.getFirst());
|
||||
LOGGER.fatal(CORE, "Failed to initialize mod containers", loadingExceptions.get(0));
|
||||
statusConsumer.ifPresent(c->c.accept("ERROR DURING MOD LOADING"));
|
||||
modList.setLoadedMods(Collections.emptyList());
|
||||
loadingStateValid = false;
|
||||
@@ -192,28 +181,22 @@ public class ModLoader
|
||||
modList.setLoadedMods(modContainers);
|
||||
this.modList = modList;
|
||||
var stateList = stateManager.getStates(ModLoadingPhase.GATHER);
|
||||
var progress = StartupMessageManager.addProgressBar("Mod Gather", stateList.stream().mapToInt(mls -> mls.size().applyAsInt(this.modList)).sum());
|
||||
for (IModLoadingState mls : stateList) {
|
||||
dispatchAndHandleError(mls, syncExecutor, parallelExecutor, periodicTask, progress);
|
||||
}
|
||||
var progress = StartupMessageManager.addProgressBar("Mod Gather", stateList.stream().mapToInt(mls -> mls.size().applyAsInt(modList)).sum());
|
||||
stateList.forEach(mls->dispatchAndHandleError(mls, syncExecutor, parallelExecutor, periodicTask, progress));
|
||||
progress.complete();
|
||||
}
|
||||
|
||||
public void loadMods(final ModWorkManager.DrivenExecutor syncExecutor, final Executor parallelExecutor, final Runnable periodicTask) {
|
||||
var stateList = stateManager.getStates(ModLoadingPhase.LOAD);
|
||||
var progress = StartupMessageManager.addProgressBar("Mod Loading", stateList.stream().mapToInt(mls -> mls.size().applyAsInt(modList)).sum());
|
||||
for (IModLoadingState mls : stateList) {
|
||||
dispatchAndHandleError(mls, syncExecutor, parallelExecutor, periodicTask, progress);
|
||||
}
|
||||
stateList.forEach(mls->dispatchAndHandleError(mls, syncExecutor, parallelExecutor, periodicTask, progress));
|
||||
progress.complete();
|
||||
}
|
||||
|
||||
public void finishMods(final ModWorkManager.DrivenExecutor syncExecutor, final Executor parallelExecutor, final Runnable periodicTask) {
|
||||
var stateList = stateManager.getStates(ModLoadingPhase.COMPLETE);
|
||||
var progress = StartupMessageManager.addProgressBar("Mod Complete", stateList.stream().mapToInt(mls -> mls.size().applyAsInt(modList)).sum());
|
||||
for (IModLoadingState mls : stateList) {
|
||||
dispatchAndHandleError(mls, syncExecutor, parallelExecutor, periodicTask, progress);
|
||||
}
|
||||
stateList.forEach(mls->dispatchAndHandleError(mls, syncExecutor, parallelExecutor, periodicTask, progress));
|
||||
statusConsumer.ifPresent(c->c.accept(String.format("Mod loading complete - %d mods loaded", this.modList.size())));
|
||||
progress.complete();
|
||||
}
|
||||
@@ -223,15 +206,10 @@ public class ModLoader
|
||||
LOGGER.error("Cowardly refusing to process mod state change request from {}", state);
|
||||
return;
|
||||
}
|
||||
progressBar.label(progressBar.name() + " working");
|
||||
progressBar.label(progressBar.name()+ " working");
|
||||
syncExecutor.drive(ticker);
|
||||
|
||||
var inlineRunnable = state.inlineRunnable().orElse(null);
|
||||
if (inlineRunnable != null) handleInlineTransition(inlineRunnable, state, syncExecutor, ticker);
|
||||
|
||||
var transition = state.buildTransition(syncExecutor, parallelExecutor, progressBar).orElse(null);
|
||||
if (transition != null) waitForTransition(state, syncExecutor, ticker, transition);
|
||||
|
||||
state.inlineRunnable().ifPresent(a->this.handleInlineTransition(a, state, syncExecutor, ticker));
|
||||
state.buildTransition(syncExecutor, parallelExecutor, progressBar).ifPresent(t->waitForTransition(state, syncExecutor, ticker, t));
|
||||
completedStates.add(state);
|
||||
}
|
||||
|
||||
@@ -308,12 +286,9 @@ public class ModLoader
|
||||
try {
|
||||
final String modId = idToProviderEntry.getKey();
|
||||
final IModLanguageProvider.IModLanguageLoader languageLoader = idToProviderEntry.getValue();
|
||||
|
||||
IModInfo info = modInfoMap.get(modId);
|
||||
// throw a missing metadata error if there is no matching modid in the modInfoMap from the mods.toml file
|
||||
if (info == null)
|
||||
throw new ModLoadingException(null, ModLoadingStage.CONSTRUCT, "fml.modloading.missingmetadata", null, modId);
|
||||
|
||||
IModInfo info = Optional.ofNullable(modInfoMap.get(modId)).
|
||||
// throw a missing metadata error if there is no matching modid in the modInfoMap from the mods.toml file
|
||||
orElseThrow(()->new ModLoadingException(null, ModLoadingStage.CONSTRUCT, "fml.modloading.missingmetadata", null, modId));
|
||||
return languageLoader.loadMod(info, modFile.getScanResult(), FMLLoader.getGameLayer());
|
||||
} catch (ModLoadingException mle) {
|
||||
// exceptions are caught and added to the error list for later handling
|
||||
@@ -328,7 +303,7 @@ public class ModLoader
|
||||
* and don't want to cause extraneous crashes due to trying to do things that aren't possible in a "broken load"
|
||||
*/
|
||||
public static boolean isLoadingStateValid() {
|
||||
return loadingStateValid;
|
||||
return get().loadingStateValid;
|
||||
}
|
||||
|
||||
public boolean hasCompletedState(final String stateName) {
|
||||
|
||||
@@ -24,42 +24,36 @@ public class ModLoadingContext
|
||||
private ModLoadingStage stage;
|
||||
|
||||
/**
|
||||
* @deprecated Use the context provided by your language loader in your mod's constructor
|
||||
* @deprecated Use the context provided by your language loader in your mod's constructor.
|
||||
*/
|
||||
@Deprecated(forRemoval = true, since="1.21.1")
|
||||
@Deprecated(forRemoval = true, since = "1.21.1")
|
||||
public static ModLoadingContext get() {
|
||||
return context.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Going to be moved to ForgeHooks for Internal use.
|
||||
* @deprecated Going to be moved to ForgeHooks for internal use.
|
||||
*/
|
||||
@Deprecated(forRemoval = true, since="1.21.1")
|
||||
@Deprecated(forRemoval = true, since = "1.21.1")
|
||||
public void setActiveContainer(final ModContainer container) {
|
||||
this.activeContainer = container;
|
||||
this.languageExtension = container == null ? null : container.contextExtension.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Going to be moved to ForgeHooks for Internal use.
|
||||
* Going to be moved to ForgeHooks for internal use.
|
||||
* @deprecated Override/Use {@link ModLoadingContext#getContainer()}
|
||||
*/
|
||||
@Deprecated(forRemoval = true, since="1.21.1")
|
||||
@Deprecated(forRemoval = true, since = "1.21.1")
|
||||
public ModContainer getActiveContainer() {
|
||||
return activeContainer == null ? ModList.get().getModContainerById("minecraft").orElseThrow(()->new RuntimeException("Where is minecraft???!")) : activeContainer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Going to be moved to ForgeHooks for Internal use.
|
||||
*/
|
||||
@Deprecated(forRemoval = true, since="1.21.1")
|
||||
@Deprecated(forRemoval = true, since = "1.21.1")
|
||||
public String getActiveNamespace() {
|
||||
return activeContainer == null ? "minecraft" : activeContainer.getNamespace();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@link ModLoadingContext#getActiveContainer()} by default.
|
||||
*/
|
||||
public ModContainer getContainer() {
|
||||
return getActiveContainer();
|
||||
}
|
||||
@@ -134,7 +128,6 @@ public class ModLoadingContext
|
||||
getContainer().addConfig(new ModConfig(type, spec, getContainer(), fileName));
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T extension() {
|
||||
return (T)languageExtension;
|
||||
|
||||
@@ -39,10 +39,7 @@ public record ModLoadingState(String name, String previous,
|
||||
final ProgressMeter progressBar,
|
||||
final Function<Executor, CompletableFuture<Void>> preSyncTask,
|
||||
final Function<Executor, CompletableFuture<Void>> postSyncTask) {
|
||||
var transition = this.transition.orElse(null);
|
||||
return transition == null
|
||||
? Optional.empty()
|
||||
: Optional.ofNullable(transition.build(name, syncExecutor, parallelExecutor, progressBar, preSyncTask, postSyncTask));
|
||||
return transition.map(t -> t.build(name, syncExecutor, parallelExecutor, progressBar, preSyncTask, postSyncTask));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -16,13 +16,15 @@ import java.util.stream.Collectors;
|
||||
|
||||
@SuppressWarnings("UnstableApiUsage")
|
||||
public class ModStateManager {
|
||||
static ModStateManager INSTANCE;
|
||||
private final EnumMap<ModLoadingPhase, List<IModLoadingState>> stateMap;
|
||||
|
||||
public ModStateManager() {
|
||||
INSTANCE = this;
|
||||
final var sp = ServiceLoader.load(FMLLoader.getGameLayer(), IModStateProvider.class);
|
||||
this.stateMap = ServiceLoaderUtils.streamWithErrorHandling(sp, sce->{})
|
||||
.map(IModStateProvider::getAllStates)
|
||||
.flatMap(List::stream)
|
||||
.<IModLoadingState>mapMulti(Iterable::forEach)
|
||||
.collect(Collectors.groupingBy(IModLoadingState::phase, ()->new EnumMap<>(ModLoadingPhase.class), Collectors.toUnmodifiableList()));
|
||||
}
|
||||
|
||||
@@ -34,16 +36,14 @@ public class ModStateManager {
|
||||
var dummy = ModLoadingState.empty("", "", phase);
|
||||
nodes.forEach(graph::addNode);
|
||||
graph.addNode(dummy);
|
||||
for (IModLoadingState node : nodes) {
|
||||
graph.putEdge(lookup.getOrDefault(node.previous(), dummy), node);
|
||||
}
|
||||
nodes.forEach(n->graph.putEdge(lookup.getOrDefault(n.previous(), dummy), n));
|
||||
return TopologicalSort.topologicalSort(graph, Comparator.comparingInt(nodes::indexOf)).stream().filter(st->st!=dummy).toList();
|
||||
}
|
||||
|
||||
public IModLoadingState findState(final String stateName) {
|
||||
return stateMap.values()
|
||||
.stream()
|
||||
.flatMap(List::stream)
|
||||
.flatMap(Collection::stream)
|
||||
.filter(mls -> mls.name().equals(stateName))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new IllegalArgumentException("Unknown IModLoadingState: " + stateName));
|
||||
|
||||
@@ -17,7 +17,6 @@ import static net.minecraftforge.fml.Logging.LOADING;
|
||||
public class ModWorkManager {
|
||||
private static final Logger LOGGER = LogManager.getLogger();
|
||||
private static final long PARK_TIME = TimeUnit.MILLISECONDS.toNanos(1);
|
||||
|
||||
public interface DrivenExecutor extends Executor {
|
||||
boolean selfDriven();
|
||||
boolean driveOne();
|
||||
@@ -34,11 +33,8 @@ public class ModWorkManager {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private record SyncExecutor(ConcurrentLinkedDeque<Runnable> tasks) implements DrivenExecutor {
|
||||
public SyncExecutor() {
|
||||
this(new ConcurrentLinkedDeque<>());
|
||||
}
|
||||
private static class SyncExecutor implements DrivenExecutor {
|
||||
private ConcurrentLinkedDeque<Runnable> tasks = new ConcurrentLinkedDeque<>();
|
||||
|
||||
@Override
|
||||
public boolean driveOne() {
|
||||
@@ -60,7 +56,13 @@ public class ModWorkManager {
|
||||
}
|
||||
}
|
||||
|
||||
private record WrappingExecutor(Executor wrapped) implements DrivenExecutor {
|
||||
private static class WrappingExecutor implements DrivenExecutor {
|
||||
private final Executor wrapped;
|
||||
|
||||
public WrappingExecutor(final Executor executor) {
|
||||
this.wrapped = executor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean selfDriven() {
|
||||
return true;
|
||||
@@ -77,9 +79,11 @@ public class ModWorkManager {
|
||||
}
|
||||
}
|
||||
|
||||
private static final SyncExecutor syncExecutor = new SyncExecutor();
|
||||
private static SyncExecutor syncExecutor;
|
||||
|
||||
public static DrivenExecutor syncExecutor() {
|
||||
if (syncExecutor == null)
|
||||
syncExecutor = new SyncExecutor();
|
||||
return syncExecutor;
|
||||
}
|
||||
|
||||
@@ -87,8 +91,14 @@ public class ModWorkManager {
|
||||
return new WrappingExecutor(executor);
|
||||
}
|
||||
|
||||
private static ForkJoinPool parallelThreadPool;
|
||||
public static Executor parallelExecutor() {
|
||||
return LazyInit.PARALLEL_EXECUTOR;
|
||||
if (parallelThreadPool == null) {
|
||||
final int loadingThreadCount = FMLConfig.getIntConfigValue(FMLConfig.ConfigValue.MAX_THREADS);
|
||||
LOGGER.debug(LOADING, "Using {} threads for parallel mod-loading", loadingThreadCount);
|
||||
parallelThreadPool = new ForkJoinPool(loadingThreadCount, ModWorkManager::newForkJoinWorkerThread, null, false);
|
||||
}
|
||||
return parallelThreadPool;
|
||||
}
|
||||
|
||||
private static ForkJoinWorkerThread newForkJoinWorkerThread(ForkJoinPool pool) {
|
||||
@@ -99,14 +109,4 @@ public class ModWorkManager {
|
||||
return thread;
|
||||
}
|
||||
|
||||
private static final class LazyInit {
|
||||
private LazyInit() {}
|
||||
private static final ForkJoinPool PARALLEL_EXECUTOR;
|
||||
|
||||
static {
|
||||
final int loadingThreadCount = FMLConfig.getIntConfigValue(FMLConfig.ConfigValue.MAX_THREADS);
|
||||
LOGGER.debug(LOADING, "Using {} threads for parallel mod-loading", loadingThreadCount);
|
||||
PARALLEL_EXECUTOR = new ForkJoinPool(loadingThreadCount, ModWorkManager::newForkJoinWorkerThread, null, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,7 @@ public final class OptionalMod<T>
|
||||
return new OptionalMod<>(modId);
|
||||
}
|
||||
|
||||
private static final OptionalMod<?> EMPTY = new OptionalMod<>(true);
|
||||
private static OptionalMod<?> EMPTY = new OptionalMod<>(true);
|
||||
private static <T> OptionalMod<T> empty() {
|
||||
@SuppressWarnings("unchecked")
|
||||
OptionalMod<T> t = (OptionalMod<T>) EMPTY;
|
||||
@@ -213,8 +213,8 @@ public final class OptionalMod<T>
|
||||
public boolean equals(Object obj)
|
||||
{
|
||||
if (this == obj) return true;
|
||||
if (obj instanceof OptionalMod<?> optionalMod) {
|
||||
return Objects.equals(optionalMod.modId, modId);
|
||||
if (obj instanceof OptionalMod) {
|
||||
return Objects.equals(((OptionalMod)obj).modId, modId);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -14,7 +14,6 @@ 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;
|
||||
@@ -26,66 +25,9 @@ import static net.minecraftforge.fml.config.ConfigTracker.CONFIG;
|
||||
|
||||
public class ConfigFileTypeHandler {
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
static 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());
|
||||
@@ -95,7 +37,7 @@ public class ConfigFileTypeHandler {
|
||||
onFileNotFound((newfile, configFormat)-> setupConfigFile(c, newfile, configFormat)).
|
||||
writingMode(WritingMode.REPLACE).
|
||||
build();
|
||||
LOGGER.debug(CONFIG, "Built TOML config for {}", configPath);
|
||||
LOGGER.debug(CONFIG, "Built TOML config for {}", configPath.toString());
|
||||
try
|
||||
{
|
||||
configData.load();
|
||||
@@ -104,9 +46,13 @@ public class ConfigFileTypeHandler {
|
||||
{
|
||||
throw new ConfigLoadingException(c, ex);
|
||||
}
|
||||
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);
|
||||
LOGGER.debug(CONFIG, "Loaded TOML config file {}", configPath.toString());
|
||||
try {
|
||||
FileWatcher.defaultInstance().addWatch(configPath, new ConfigWatcher(c, configData, Thread.currentThread().getContextClassLoader()));
|
||||
LOGGER.debug(CONFIG, "Watching TOML config file {} for changes", configPath.toString());
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Couldn't watch config file", e);
|
||||
}
|
||||
return configData;
|
||||
};
|
||||
}
|
||||
@@ -114,13 +60,13 @@ public class ConfigFileTypeHandler {
|
||||
public void unload(Path configBasePath, ModConfig config) {
|
||||
Path configPath = configBasePath.resolve(config.getFileName());
|
||||
try {
|
||||
this.getWatcher().removeWatch(configPath);
|
||||
FileWatcher.defaultInstance().removeWatch(configBasePath.resolve(config.getFileName()));
|
||||
} catch (RuntimeException e) {
|
||||
LOGGER.error("Failed to remove config {} from tracker!", configPath, e);
|
||||
LOGGER.error("Failed to remove config {} from tracker!", configPath.toString(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean setupConfigFile(final ModConfig modConfig, final Path file, final ConfigFormat<?> conf) throws IOException {
|
||||
private boolean setupConfigFile(final ModConfig modConfig, final Path file, final ConfigFormat<?> conf) throws IOException {
|
||||
if (!Files.isDirectory(file.getParent())) {
|
||||
Files.createDirectories(file.getParent());
|
||||
}
|
||||
@@ -167,16 +113,17 @@ public class ConfigFileTypeHandler {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ConfigFileTypeHandler[" + (type != null ? type : "UNKNOWN") + "]";
|
||||
}
|
||||
private static class ConfigWatcher implements Runnable {
|
||||
private final ModConfig modConfig;
|
||||
private final CommentedFileConfig commentedFileConfig;
|
||||
private final ClassLoader realClassLoader;
|
||||
|
||||
ConfigWatcher(final ModConfig modConfig, final CommentedFileConfig commentedFileConfig, final ClassLoader classLoader) {
|
||||
this.modConfig = modConfig;
|
||||
this.commentedFileConfig = commentedFileConfig;
|
||||
this.realClassLoader = classLoader;
|
||||
}
|
||||
|
||||
private record ConfigWatcher(
|
||||
ModConfig modConfig,
|
||||
CommentedFileConfig commentedFileConfig,
|
||||
ClassLoader realClassLoader
|
||||
) implements Runnable {
|
||||
@Override
|
||||
public void run() {
|
||||
// Force the regular classloader onto the special thread
|
||||
|
||||
@@ -8,9 +8,6 @@ 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;
|
||||
@@ -50,32 +47,15 @@ public class ConfigTracker {
|
||||
|
||||
public void loadConfigs(ModConfig.Type type, Path configBasePath) {
|
||||
LOGGER.debug(CONFIG, "Loading configs type {}", type);
|
||||
for (ModConfig config : this.configSets.get(type)) {
|
||||
openConfig(config, configBasePath);
|
||||
}
|
||||
this.configSets.get(type).forEach(config -> openConfig(config, configBasePath));
|
||||
}
|
||||
|
||||
// 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();
|
||||
this.configSets.get(type).forEach(config -> closeConfig(config, configBasePath));
|
||||
}
|
||||
|
||||
// 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) {
|
||||
private void openConfig(final ModConfig config, final Path configBasePath) {
|
||||
LOGGER.trace(CONFIG, "Loading config file type {} at {} for {}", config.getType(), config.getFileName(), config.getModId());
|
||||
final CommentedFileConfig configData = config.getHandler().reader(configBasePath).apply(config);
|
||||
config.setConfigData(configData);
|
||||
@@ -83,7 +63,7 @@ public class ConfigTracker {
|
||||
config.save();
|
||||
}
|
||||
|
||||
private static void closeConfig(final ModConfig config, final Path configBasePath) {
|
||||
private void closeConfig(final ModConfig config, final Path configBasePath) {
|
||||
if (config.getConfigData() != null) {
|
||||
LOGGER.trace(CONFIG, "Closing config file type {} at {} for {}", config.getType(), config.getFileName(), config.getModId());
|
||||
// stop the filewatcher before we save the file and close it, so reload doesn't fire
|
||||
|
||||
@@ -24,13 +24,14 @@ public class ModConfig
|
||||
private final ModContainer container;
|
||||
private final ConfigFileTypeHandler configHandler;
|
||||
private CommentedConfig configData;
|
||||
private Callable<Void> saveHandler;
|
||||
|
||||
public ModConfig(final Type type, final IConfigSpec<?> spec, final ModContainer container, final String fileName) {
|
||||
this.type = type;
|
||||
this.spec = spec;
|
||||
this.fileName = fileName;
|
||||
this.container = container;
|
||||
this.configHandler = ConfigFileTypeHandler.get(type);
|
||||
this.configHandler = ConfigFileTypeHandler.TOML;
|
||||
ConfigTracker.INSTANCE.trackConfig(this);
|
||||
}
|
||||
|
||||
|
||||
+15
-21
@@ -22,7 +22,9 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.awt.Desktop;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.net.URI;
|
||||
@@ -300,6 +302,13 @@ public class DisplayWindow implements ImmediateWindowProvider {
|
||||
}
|
||||
|
||||
private void crashElegantly(String errorDetails) {
|
||||
String qrText;
|
||||
try (var is = new BufferedReader(new InputStreamReader(getClass().getResourceAsStream("/glfailure.txt")))) {
|
||||
qrText = is.lines().collect(Collectors.joining("\n"));
|
||||
} catch (IOException ioe) {
|
||||
qrText = "";
|
||||
}
|
||||
|
||||
StringBuilder msgBuilder = new StringBuilder(2000);
|
||||
msgBuilder.append("Failed to initialize graphics window with current settings.\n");
|
||||
msgBuilder.append("\n\n");
|
||||
@@ -398,13 +407,13 @@ public class DisplayWindow implements ImmediateWindowProvider {
|
||||
if (showHelpLog && versidx == 0) {
|
||||
LOGGER.info("""
|
||||
If this message is the only thing at the bottom of your log before a crash, you probably have a driver issue.
|
||||
|
||||
|
||||
Possible solutions:
|
||||
A) Make sure Minecraft is set to prefer high performance graphics in the OS and/or driver control panel
|
||||
B) Check for driver updates on the graphics brand's website
|
||||
C) Try reinstalling your graphics drivers
|
||||
D) If still not working after trying all of the above, ask for further help on the Forge forums or Discord
|
||||
|
||||
|
||||
You can safely ignore this message if the game starts up successfully.""");
|
||||
}
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, GL_VERSIONS[versidx][0]); // we try our versions one at a time
|
||||
@@ -603,25 +612,10 @@ public class DisplayWindow implements ImmediateWindowProvider {
|
||||
|
||||
@Override
|
||||
public void updateModuleReads(final ModuleLayer layer) {
|
||||
final var FORGE_MODULE = "net.minecraftforge.forge";
|
||||
var forge_module = layer.findModule(FORGE_MODULE).orElse(null);
|
||||
if (forge_module == null)
|
||||
throw new IllegalStateException("Could not find " + FORGE_MODULE + " in " + layer);
|
||||
|
||||
getClass().getModule().addReads(forge_module);
|
||||
|
||||
|
||||
var clz = Class.forName(forge_module, "net.minecraftforge.client.loading.ForgeLoadingOverlay");
|
||||
|
||||
for (var mtd : clz.getDeclaredMethods()) {
|
||||
if (Modifier.isStatic(mtd.getModifiers()) && "newInstance".equals(mtd.getName())) {
|
||||
this.loadingOverlay = mtd;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (loadingOverlay == null)
|
||||
throw new IllegalStateException("Could not find static newInstace method in " + clz.getName());
|
||||
var fm = layer.findModule("forge").orElseThrow();
|
||||
getClass().getModule().addReads(fm);
|
||||
var clz = FMLLoader.getGameLayer().findModule("forge").map(l->Class.forName(l, "net.minecraftforge.client.loading.ForgeLoadingOverlay")).orElseThrow();
|
||||
loadingOverlay = Arrays.stream(clz.getDeclaredMethods()).filter(m-> Modifier.isStatic(m.getModifiers()) && m.getName().equals("newInstance")).findFirst().orElseThrow();
|
||||
}
|
||||
|
||||
public int getFramebufferTextureId() {
|
||||
|
||||
@@ -25,7 +25,7 @@ public class SimpleFont {
|
||||
private final int lineSpacing;
|
||||
private final int descent;
|
||||
private final int GLYPH_COUNT = 127-32;
|
||||
private final Glyph[] glyphs;
|
||||
private Glyph[] glyphs;
|
||||
|
||||
private record Glyph(char c, int charwidth, int[] pos, float[] uv) {
|
||||
Pos loadQuad(Pos pos, int colour, SimpleBufferBuilder bb) {
|
||||
|
||||
+25
-11
@@ -49,7 +49,8 @@ public class CapabilityTokenSubclass implements ILaunchPluginService {
|
||||
private static final EnumSet<Phase> NAY = EnumSet.noneOf(Phase.class);
|
||||
|
||||
@Override
|
||||
public EnumSet<Phase> handlesClass(Type classType, boolean isEmpty) {
|
||||
public EnumSet<Phase> handlesClass(Type classType, boolean isEmpty)
|
||||
{
|
||||
if (isEmpty)
|
||||
return NAY;
|
||||
|
||||
@@ -61,33 +62,43 @@ public class CapabilityTokenSubclass implements ILaunchPluginService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int processClassWithFlags(final Phase phase, final ClassNode classNode, final Type classType, final String reason) {
|
||||
if (CAP_INJECT.equals(classNode.name)) {
|
||||
for (MethodNode mtd : classNode.methods) {
|
||||
if (FUNC_NAME.equals(mtd.name) && FUNC_DESC.equals(mtd.desc)) {
|
||||
public int processClassWithFlags(final Phase phase, final ClassNode classNode, final Type classType, final String reason)
|
||||
{
|
||||
if (CAP_INJECT.equals(classNode.name))
|
||||
{
|
||||
for (MethodNode mtd : classNode.methods)
|
||||
{
|
||||
if (FUNC_NAME.equals(mtd.name) && FUNC_DESC.equals(mtd.desc))
|
||||
{
|
||||
mtd.access &= ~Opcodes.ACC_FINAL; // We have it final in code so people don't override it, cuz that'd be stupid, and make our transformer more complicated.
|
||||
}
|
||||
}
|
||||
return ComputeFlags.SIMPLE_REWRITE;
|
||||
} else if (CAP_INJECT.equals(classNode.superName)) {
|
||||
}
|
||||
else if (CAP_INJECT.equals(classNode.superName))
|
||||
{
|
||||
Holder cls = new Holder();
|
||||
|
||||
SignatureReader reader = new SignatureReader(classNode.signature); // Having a node version of this would probably be useful.
|
||||
reader.accept(new SignatureVisitor(Opcodes.ASM9) {
|
||||
reader.accept(new SignatureVisitor(Opcodes.ASM9)
|
||||
{
|
||||
final Deque<String> stack = new ArrayDeque<>();
|
||||
|
||||
@Override
|
||||
public void visitClassType(final String name) {
|
||||
public void visitClassType(final String name)
|
||||
{
|
||||
stack.push(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitInnerClassType(final String name) {
|
||||
public void visitInnerClassType(final String name)
|
||||
{
|
||||
stack.push(stack.pop() + '$' + name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitEnd() {
|
||||
public void visitEnd()
|
||||
{
|
||||
var val = stack.pop();
|
||||
if (!stack.isEmpty() && CAP_INJECT.equals(stack.peek()))
|
||||
cls.value = val;
|
||||
@@ -102,7 +113,9 @@ public class CapabilityTokenSubclass implements ILaunchPluginService {
|
||||
mtd.visitInsn(Opcodes.ARETURN);
|
||||
mtd.visitEnd();
|
||||
return ComputeFlags.COMPUTE_MAXS;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
return ComputeFlags.NO_REWRITE;
|
||||
}
|
||||
}
|
||||
@@ -110,4 +123,5 @@ public class CapabilityTokenSubclass implements ILaunchPluginService {
|
||||
private static final class Holder {
|
||||
String value;
|
||||
}
|
||||
|
||||
}
|
||||
+2
-2
@@ -35,7 +35,7 @@ public class ObjectHolderDefinalize implements ILaunchPluginService {
|
||||
new VanillaObjectHolderData("net.minecraft.world.effect.MobEffects", "mob_effect", "net.minecraft.world.effect.MobEffect"),
|
||||
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()));
|
||||
).collect(Collectors.toMap(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());
|
||||
@@ -73,7 +73,7 @@ public class ObjectHolderDefinalize implements ILaunchPluginService {
|
||||
return YAY;
|
||||
}
|
||||
|
||||
private static boolean hasHolder(List<AnnotationNode> lst) {
|
||||
private boolean hasHolder(List<AnnotationNode> lst) {
|
||||
return lst != null && lst.stream().anyMatch(n -> n.desc.equals(OBJECT_HOLDER));
|
||||
}
|
||||
|
||||
|
||||
@@ -17,9 +17,11 @@ import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.fml.loading.targets.CommonLaunchHandler;
|
||||
import net.minecraftforge.forgespi.Environment;
|
||||
import net.minecraftforge.forgespi.coremod.ICoreModProvider;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -34,6 +36,7 @@ import static net.minecraftforge.fml.loading.LogMarkers.SCAN;
|
||||
public class FMLLoader {
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
private static AccessTransformerService accessTransformer;
|
||||
private static ModDiscoverer modDiscoverer;
|
||||
private static ICoreModProvider coreModProvider;
|
||||
private static LanguageLoadingProvider languageLoadingProvider;
|
||||
private static Dist dist;
|
||||
@@ -62,7 +65,7 @@ public class FMLLoader {
|
||||
/*eventBus =*/ getPlugin(env, "eventbus", "1.0", "EventBus");
|
||||
runtimeDistCleaner = getPlugin(env, "runtimedistcleaner", "1.0", "RuntimeDistCleaner");
|
||||
coreModProvider = getSingleService(ICoreModProvider.class, "CoreMod");
|
||||
LOGGER.debug(CORE, "FML found CoreMod version : {}", JarVersionLookupHandler.getInfo(coreModProvider.getClass()).impl().version().orElse("MISSING"));
|
||||
LOGGER.debug(CORE,"FML found CoreMod version : {}", JarVersionLookupHandler.getInfo(coreModProvider.getClass()).impl().version().orElse("MISSING"));
|
||||
checkPackage(Environment.class, "2.0", "ForgeSPI");
|
||||
|
||||
try {
|
||||
@@ -74,15 +77,14 @@ public class FMLLoader {
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <T> T getPlugin(IEnvironment env, String id, String version, String name) throws IncompatibleEnvironmentException {
|
||||
var plugin = env.findLaunchPlugin(id).orElse(null);
|
||||
if (plugin == null) {
|
||||
@SuppressWarnings("unchecked")
|
||||
var plugin = (T)env.findLaunchPlugin(id).orElseThrow(() -> {
|
||||
LOGGER.error(CORE, "{} library is missing, we need this to run", name);
|
||||
throw new IncompatibleEnvironmentException("Missing " + name + ", cannot run");
|
||||
}
|
||||
return new IncompatibleEnvironmentException("Missing " + name + ", cannot run");
|
||||
});
|
||||
checkPackage(plugin.getClass(), version, name);
|
||||
return (T) plugin;
|
||||
return plugin;
|
||||
}
|
||||
|
||||
private static void checkPackage(Class<?> cls, String version, String name) throws IncompatibleEnvironmentException {
|
||||
@@ -144,20 +146,20 @@ public class FMLLoader {
|
||||
}
|
||||
commonLaunchHandler = (CommonLaunchHandler)launchHandler.get();
|
||||
launchHandlerName = launchHandler.get().name();
|
||||
gamePath = environment.getProperty(IEnvironment.Keys.GAMEDIR.get()).orElse(Path.of(".").toAbsolutePath());
|
||||
gamePath = environment.getProperty(IEnvironment.Keys.GAMEDIR.get()).orElse(Paths.get(".").toAbsolutePath());
|
||||
|
||||
naming = commonLaunchHandler.getNaming();
|
||||
dist = commonLaunchHandler.getDist();
|
||||
production = commonLaunchHandler.isProduction();
|
||||
|
||||
accessTransformer.getExtension().accept(Map.entry(naming, "srg"));
|
||||
accessTransformer.getExtension().accept(Pair.of(naming, "srg"));
|
||||
|
||||
runtimeDistCleaner.getExtension().accept(dist);
|
||||
}
|
||||
|
||||
public static List<ITransformationService.Resource> beginModScan(final Map<String,?> arguments) {
|
||||
LOGGER.debug(SCAN,"Scanning for Mod Locators");
|
||||
var modDiscoverer = new ModDiscoverer(arguments);
|
||||
modDiscoverer = new ModDiscoverer(arguments);
|
||||
modValidator = modDiscoverer.discoverMods();
|
||||
var pluginResources = modValidator.getPluginResources();
|
||||
return List.of(pluginResources);
|
||||
@@ -179,6 +181,10 @@ public class FMLLoader {
|
||||
return languageLoadingProvider;
|
||||
}
|
||||
|
||||
static ModDiscoverer getModDiscoverer() {
|
||||
return modDiscoverer;
|
||||
}
|
||||
|
||||
public static CommonLaunchHandler getLaunchHandler() {
|
||||
return commonLaunchHandler;
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ import static net.minecraftforge.fml.loading.LogMarkers.CORE;
|
||||
|
||||
public class FMLServiceProvider implements ITransformationService {
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
private final Map<String, Object> arguments = new HashMap<>();
|
||||
private Map<String, Object> arguments;
|
||||
|
||||
public FMLServiceProvider() {
|
||||
var markers = System.getProperty("forge.logging.markers", "").split(",");
|
||||
@@ -45,6 +45,7 @@ public class FMLServiceProvider implements ITransformationService {
|
||||
FMLConfig.load();
|
||||
LOGGER.debug(CORE, "Preparing ModFile");
|
||||
environment.computePropertyIfAbsent(Environment.Keys.MODFILEFACTORY.get(), k->ModFile::new);
|
||||
arguments = new HashMap<>();
|
||||
LOGGER.debug(CORE, "Preparing launch handler");
|
||||
FMLLoader.setupLaunchHandler(environment, arguments);
|
||||
FMLEnvironment.setupInteropEnvironment(environment);
|
||||
@@ -67,6 +68,14 @@ public class FMLServiceProvider implements ITransformationService {
|
||||
FMLLoader.onInitialLoad(environment, otherServices);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void arguments(BiFunction<String, String, OptionSpecBuilder> argumentBuilder) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void argumentValues(OptionResult option) {
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
@Override
|
||||
public @NotNull List<ITransformer> transformers() {
|
||||
|
||||
@@ -140,7 +140,7 @@ public class ImmediateWindowHandler {
|
||||
|
||||
@Override
|
||||
public void updateModuleReads(final ModuleLayer layer) {
|
||||
var fm = layer.findModule("net.minecraftforge.forge");
|
||||
var fm = layer.findModule("forge");
|
||||
if (fm.isPresent()) {
|
||||
getClass().getModule().addReads(fm.get());
|
||||
var clz = fm.map(l -> Class.forName(l, "net.minecraftforge.client.loading.NoVizFallback")).orElseThrow();
|
||||
|
||||
@@ -78,13 +78,9 @@ public class LoadingModList
|
||||
|
||||
public void addAccessTransformers()
|
||||
{
|
||||
for (ModFileInfo modFile : modFiles) {
|
||||
ModFile mod = modFile.getFile();
|
||||
var at = mod.getAccessTransformer().orElse(null);
|
||||
if (at != null) {
|
||||
FMLLoader.addAccessTransformer(at, mod);
|
||||
}
|
||||
}
|
||||
modFiles.stream()
|
||||
.map(ModFileInfo::getFile)
|
||||
.forEach(mod -> mod.getAccessTransformer().ifPresent(path -> FMLLoader.addAccessTransformer(path, mod)));
|
||||
}
|
||||
|
||||
public void addForScanning(BackgroundScanHandler backgroundScanHandler)
|
||||
|
||||
@@ -36,7 +36,7 @@ public class MCPNamingService implements INameMappingService {
|
||||
|
||||
@Override
|
||||
public Map.Entry<String, String> understanding() {
|
||||
return Map.entry("srg", "mcp");
|
||||
return Pair.of("srg", "mcp");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -34,7 +34,7 @@ public class MavenCoordinateResolver {
|
||||
(!extension.isEmpty() ? "." + extension : ".jar");
|
||||
|
||||
String[] groups = groupId.split("\\.");
|
||||
Path result = Path.of(groups[0]);
|
||||
Path result = Paths.get(groups[0]);
|
||||
for (int i = 1; i < groups.length; i++) {
|
||||
result = result.resolve(groups[i]);
|
||||
}
|
||||
|
||||
@@ -57,13 +57,13 @@ public class ModSorter
|
||||
// Note this will never actually throw an error because the duplicate checks are done in ModDiscovererer before we get to this phase
|
||||
// So all this is really doing is wasting time.
|
||||
// But i'm leaving it here until I rewrite all of cpw's mod loading code because its such a clusterfuck.
|
||||
return LoadingModList.of(ms.systemMods, ms.systemMods.stream().map(mf->(ModInfo)mf.getModInfos().get(0)).toList(), e);
|
||||
return LoadingModList.of(ms.systemMods, ms.systemMods.stream().map(mf->(ModInfo)mf.getModInfos().get(0)).collect(toList()), e);
|
||||
}
|
||||
// try and validate dependencies
|
||||
final List<ExceptionData> failedList = Stream.concat(ms.verifyDependencyVersions().stream(), errors.stream()).toList();
|
||||
// if we miss one or the other, we abort now
|
||||
if (!failedList.isEmpty()) {
|
||||
return LoadingModList.of(ms.systemMods, ms.systemMods.stream().map(mf->(ModInfo)mf.getModInfos().get(0)).toList(), new EarlyLoadingException("failure to validate mod list", null, failedList));
|
||||
return LoadingModList.of(ms.systemMods, ms.systemMods.stream().map(mf->(ModInfo)mf.getModInfos().get(0)).collect(toList()), new EarlyLoadingException("failure to validate mod list", null, failedList));
|
||||
} else {
|
||||
// Otherwise, lets try and sort the modlist and proceed
|
||||
EarlyLoadingException earlyLoadingException = null;
|
||||
@@ -89,9 +89,9 @@ public class ModSorter
|
||||
infos.keySet().forEach(graph::addNode);
|
||||
modFiles.stream()
|
||||
.map(ModFile::getModInfos)
|
||||
.flatMap(List::stream)
|
||||
.<IModInfo>mapMulti(Iterable::forEach)
|
||||
.map(IModInfo::getDependencies)
|
||||
.<IModInfo.ModVersion>flatMap(List::stream)
|
||||
.<IModInfo.ModVersion>mapMulti(Iterable::forEach)
|
||||
.forEach(dep -> addDependency(graph, dep));
|
||||
|
||||
final List<ModFileInfo> sorted;
|
||||
@@ -107,7 +107,7 @@ public class ModSorter
|
||||
LOGGER.error(LOADING, "Mod Sorting failed.\nDetected Cycles: {}\n", cycles);
|
||||
}
|
||||
var dataList = cycles.stream()
|
||||
.flatMap(Set::stream)
|
||||
.<ModFileInfo>mapMulti(Iterable::forEach)
|
||||
.<IModInfo>mapMulti((mf,c)->mf.getMods().forEach(c))
|
||||
.map(IModInfo::getModId)
|
||||
.map(list -> new ExceptionData("fml.modloading.cycle", list))
|
||||
@@ -116,7 +116,7 @@ public class ModSorter
|
||||
}
|
||||
this.sortedList = sorted.stream()
|
||||
.map(ModFileInfo::getMods)
|
||||
.flatMap(List::stream)
|
||||
.<IModInfo>mapMulti(Iterable::forEach)
|
||||
.map(ModInfo.class::cast)
|
||||
.collect(toList());
|
||||
this.modFiles = sorted.stream()
|
||||
@@ -126,14 +126,12 @@ public class ModSorter
|
||||
|
||||
private void addDependency(MutableGraph<ModFileInfo> topoGraph, IModInfo.ModVersion dep)
|
||||
{
|
||||
final ModFileInfo self = (ModFileInfo)dep.getOwner().getOwningFile();
|
||||
final IModInfo targetModInfo = modIdNameLookup.get(dep.getModId());
|
||||
// soft dep that doesn't exist. Just return. No edge required.
|
||||
if (targetModInfo == null || !(targetModInfo.getOwningFile() instanceof final ModFileInfo target)) return;
|
||||
|
||||
final ModFileInfo self = (ModFileInfo)dep.getOwner().getOwningFile();
|
||||
if (self == target)
|
||||
return; // in case a jar has two mods that have dependencies between
|
||||
|
||||
switch (dep.getOrdering()) {
|
||||
case BEFORE -> topoGraph.putEdge(self, target);
|
||||
case AFTER -> topoGraph.putEdge(target, self);
|
||||
@@ -167,7 +165,7 @@ public class ModSorter
|
||||
var container = modFilesByFirstId.get(systemMod);
|
||||
if (container != null && !container.isEmpty()) {
|
||||
LOGGER.debug("Found system mod: {}", systemMod);
|
||||
this.systemMods.add(container.getFirst());
|
||||
this.systemMods.add((ModFile) container.get(0));
|
||||
} else {
|
||||
throw new IllegalStateException("Failed to find system mod: " + systemMod);
|
||||
}
|
||||
@@ -178,23 +176,23 @@ public class ModSorter
|
||||
{
|
||||
final var modVersions = modFiles.stream()
|
||||
.map(ModFile::getModInfos)
|
||||
.flatMap(List::stream)
|
||||
.<IModInfo>mapMulti(Iterable::forEach)
|
||||
.collect(toMap(IModInfo::getModId, IModInfo::getVersion));
|
||||
|
||||
final var modVersionDependencies = modFiles.stream()
|
||||
.map(ModFile::getModInfos)
|
||||
.flatMap(List::stream)
|
||||
.<IModInfo>mapMulti(Iterable::forEach)
|
||||
.collect(groupingBy(Function.identity(), flatMapping(e -> e.getDependencies().stream(), toList())));
|
||||
|
||||
final var modRequirements = modVersionDependencies.values().stream()
|
||||
.<IModInfo.ModVersion>flatMap(List::stream)
|
||||
.<IModInfo.ModVersion>mapMulti(Iterable::forEach)
|
||||
.filter(mv -> mv.getSide().isCorrectSide())
|
||||
.collect(toSet());
|
||||
|
||||
final long mandatoryRequired = modRequirements.stream().filter(IModInfo.ModVersion::isMandatory).count();
|
||||
LOGGER.debug(LOADING, "Found {} mod requirements ({} mandatory, {} optional)", modRequirements.size(), mandatoryRequired, modRequirements.size() - mandatoryRequired);
|
||||
final var missingVersions = modRequirements.stream()
|
||||
.filter(mv -> (mv.isMandatory() || modVersions.containsKey(mv.getModId())) && !modVersionContained(mv, modVersions))
|
||||
.filter(mv -> (mv.isMandatory() || modVersions.containsKey(mv.getModId())) && this.modVersionNotContained(mv, modVersions))
|
||||
.collect(toSet());
|
||||
final long mandatoryMissing = missingVersions.stream().filter(IModInfo.ModVersion::isMandatory).count();
|
||||
LOGGER.debug(LOADING, "Found {} mod requirements missing ({} mandatory, {} optional)", missingVersions.size(), mandatoryMissing, missingVersions.size() - mandatoryMissing);
|
||||
@@ -242,13 +240,9 @@ public class ModSorter
|
||||
);
|
||||
}
|
||||
|
||||
private static boolean modVersionContained(IModInfo.ModVersion mv, Map<String, ArtifactVersion> modVersions) {
|
||||
var modId = mv.getModId();
|
||||
var range = mv.getVersionRange();
|
||||
if (modVersions.containsKey(modId)
|
||||
&& (range.containsVersion(modVersions.get(modId)) || modVersions.get(modId).toString().equals("0.0NONE")))
|
||||
return true;
|
||||
|
||||
return VersionSupportMatrix.testVersionSupportMatrix(mv.getVersionRange(), mv.getModId(), "mod");
|
||||
private boolean modVersionNotContained(final IModInfo.ModVersion mv, final Map<String, ArtifactVersion> modVersions)
|
||||
{
|
||||
return !(VersionSupportMatrix.testVersionSupportMatrix(mv.getVersionRange(), mv.getModId(), "mod", (modId, range) -> modVersions.containsKey(modId) &&
|
||||
(range.containsVersion(modVersions.get(modId)) || modVersions.get(modId).toString().equals("0.0NONE"))));
|
||||
}
|
||||
}
|
||||
@@ -8,8 +8,14 @@ package net.minecraftforge.fml.loading;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.google.common.collect.Streams;
|
||||
import com.mojang.logging.LogUtils;
|
||||
import cpw.mods.modlauncher.serviceapi.ILaunchPluginService;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
@@ -22,177 +28,200 @@ import org.objectweb.asm.Type;
|
||||
import org.objectweb.asm.tree.AbstractInsnNode;
|
||||
import org.objectweb.asm.tree.AnnotationNode;
|
||||
import org.objectweb.asm.tree.ClassNode;
|
||||
import org.objectweb.asm.tree.FieldNode;
|
||||
import org.objectweb.asm.tree.MethodNode;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.Marker;
|
||||
import org.slf4j.MarkerFactory;
|
||||
|
||||
public class RuntimeDistCleaner implements ILaunchPluginService {
|
||||
public class RuntimeDistCleaner implements ILaunchPluginService
|
||||
{
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
private static final Marker DISTXFORM = MarkerFactory.getMarker("DISTXFORM");
|
||||
private static String DIST;
|
||||
private static final String ONLYIN = Type.getDescriptor(OnlyIn.class);
|
||||
private static final String ONLYINS = Type.getDescriptor(OnlyIns.class);
|
||||
private static final String MOD = "Lnet/minecraftforge/fml/common/Mod;";
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
public String name()
|
||||
{
|
||||
return "runtimedistcleaner";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int processClassWithFlags(final Phase phase, final ClassNode classNode, final Type classType, final String reason) {
|
||||
var changed = false;
|
||||
|
||||
var annotations = unpack(classNode.visibleAnnotations);
|
||||
|
||||
if (remove(annotations, DIST)) {
|
||||
public int processClassWithFlags(final Phase phase, final ClassNode classNode, final Type classType, final String reason)
|
||||
{
|
||||
AtomicBoolean changes = new AtomicBoolean();
|
||||
if (remove(classNode.visibleAnnotations, DIST))
|
||||
{
|
||||
LOGGER.error(DISTXFORM, "Attempted to load class {} for invalid dist {}", classNode.name, DIST);
|
||||
throw new RuntimeException("Attempted to load class "+ classNode.name + " for invalid dist "+ DIST);
|
||||
}
|
||||
|
||||
if (!FMLEnvironment.production && !annotations.isEmpty() && hasModAnnotation(classNode.visibleAnnotations)) {
|
||||
if (!FMLEnvironment.production && hasOnlyInWithModAnnotation(classNode.visibleAnnotations))
|
||||
{
|
||||
LOGGER.error(DISTXFORM, "Attempted to load class {} with @Mod and @OnlyIn/@OnlyIns annotations", classNode.name);
|
||||
throw new RuntimeException("Found @OnlyIn on @Mod class "+ classNode.name + " - this is not allowed as it causes crashes. Remove the OnlyIn and consider setting clientSideOnly=true in the root of your mods.toml instead");
|
||||
}
|
||||
|
||||
if (classNode.interfaces != null && !classNode.interfaces.isEmpty()) {
|
||||
for (var ann : annotations) {
|
||||
if (ann.intf == null || DIST.equals(ann.side))
|
||||
continue;
|
||||
|
||||
if (classNode.interfaces.remove(ann.intf)) {
|
||||
LOGGER.debug(DISTXFORM,"Removing Interface: {} implements {}", classNode.name, ann.intf);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
if (classNode.interfaces != null )
|
||||
{
|
||||
unpack(classNode.visibleAnnotations).stream()
|
||||
.filter(ann->Objects.equals(ann.desc, ONLYIN))
|
||||
.filter(ann-> ann.values.contains("_interface"))
|
||||
.filter(ann->!Objects.equals(((String[])ann.values.get(ann.values.indexOf("value") + 1))[1], DIST))
|
||||
.map(ann -> ((Type)ann.values.get(ann.values.indexOf("_interface") + 1)).getInternalName())
|
||||
.forEach(intf -> {
|
||||
if (classNode.interfaces.remove(intf)) {
|
||||
LOGGER.debug(DISTXFORM,"Removing Interface: {} implements {}", classNode.name, intf);
|
||||
changes.compareAndSet(false, true);
|
||||
}
|
||||
});
|
||||
|
||||
//Remove Class level @OnlyIn/@OnlyIns annotations, this is important if anyone gets ambitious and tries to reflect an annotation with _interface set.
|
||||
if (classNode.visibleAnnotations != null) {
|
||||
for (var itr = classNode.visibleAnnotations.iterator(); itr.hasNext(); ) {
|
||||
var ann = itr.next();
|
||||
if (ONLYIN.equals(ann.desc) || ONLYINS.equals(ann.desc)) {
|
||||
LOGGER.debug(DISTXFORM, "Removing Class Annotation: {} @{}", classNode.name, ann.desc);
|
||||
Iterator<AnnotationNode> itr = classNode.visibleAnnotations.iterator();
|
||||
while (itr.hasNext()) {
|
||||
AnnotationNode ann = itr.next();
|
||||
if (Objects.equals(ann.desc, ONLYIN) || Objects.equals(ann.desc, ONLYINS)) {
|
||||
LOGGER.debug(DISTXFORM,"Removing Class Annotation: {} @{}", classNode.name, ann.desc);
|
||||
itr.remove();
|
||||
changed = true;
|
||||
changes.compareAndSet(false, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (var itr = classNode.fields.iterator(); itr.hasNext(); ) {
|
||||
var field = itr.next();
|
||||
if (remove(unpack(field.visibleAnnotations), DIST)) {
|
||||
LOGGER.debug(DISTXFORM, "Removing field: {}.{}", classNode.name, field.name);
|
||||
itr.remove();
|
||||
changed = true;
|
||||
Iterator<FieldNode> fields = classNode.fields.iterator();
|
||||
while(fields.hasNext())
|
||||
{
|
||||
FieldNode field = fields.next();
|
||||
if (remove(field.visibleAnnotations, DIST))
|
||||
{
|
||||
LOGGER.debug(DISTXFORM,"Removing field: {}.{}", classNode.name, field.name);
|
||||
fields.remove();
|
||||
changes.compareAndSet(false, true);
|
||||
}
|
||||
}
|
||||
|
||||
var lambdaGatherer = new LambdaGatherer();
|
||||
for (var itr = classNode.methods.iterator(); itr.hasNext(); ) {
|
||||
var method = itr.next();
|
||||
if (remove(unpack(method.visibleAnnotations), DIST)) {
|
||||
LOGGER.debug(DISTXFORM, "Removing method: {}.{}{}", classNode.name, method.name, method.desc);
|
||||
itr.remove();
|
||||
LambdaGatherer lambdaGatherer = new LambdaGatherer();
|
||||
Iterator<MethodNode> methods = classNode.methods.iterator();
|
||||
while(methods.hasNext())
|
||||
{
|
||||
MethodNode method = methods.next();
|
||||
if (remove(method.visibleAnnotations, DIST))
|
||||
{
|
||||
LOGGER.debug(DISTXFORM,"Removing method: {}.{}{}", classNode.name, method.name, method.desc);
|
||||
methods.remove();
|
||||
lambdaGatherer.accept(method);
|
||||
changed = true;
|
||||
changes.compareAndSet(false, true);
|
||||
}
|
||||
}
|
||||
|
||||
// remove dynamic synthetic lambda methods that are inside of removed methods
|
||||
for (List<Handle> handles = lambdaGatherer.getDynamicLambdaHandles();
|
||||
!handles.isEmpty(); handles = lambdaGatherer.getDynamicLambdaHandles()) {
|
||||
for (List<Handle> dynamicLambdaHandles = lambdaGatherer.getDynamicLambdaHandles();
|
||||
!dynamicLambdaHandles.isEmpty(); dynamicLambdaHandles = lambdaGatherer.getDynamicLambdaHandles())
|
||||
{
|
||||
lambdaGatherer = new LambdaGatherer();
|
||||
for (var itr = classNode.methods.iterator(); itr.hasNext(); ) {
|
||||
MethodNode method = itr.next();
|
||||
if ((method.access & Opcodes.ACC_SYNTHETIC) == 0)
|
||||
continue;
|
||||
|
||||
for (var handle : handles) {
|
||||
if (method.name.equals(handle.getName()) && method.desc.equals(handle.getDesc())) {
|
||||
LOGGER.debug(DISTXFORM, "Removing lambda method: {}.{}{}", classNode.name, method.name, method.desc);
|
||||
itr.remove();
|
||||
methods = classNode.methods.iterator();
|
||||
while (methods.hasNext())
|
||||
{
|
||||
MethodNode method = methods.next();
|
||||
if ((method.access & Opcodes.ACC_SYNTHETIC) == 0) continue;
|
||||
for (Handle dynamicLambdaHandle : dynamicLambdaHandles)
|
||||
{
|
||||
if (method.name.equals(dynamicLambdaHandle.getName()) && method.desc.equals(dynamicLambdaHandle.getDesc()))
|
||||
{
|
||||
LOGGER.debug(DISTXFORM,"Removing lambda method: {}.{}{}", classNode.name, method.name, method.desc);
|
||||
methods.remove();
|
||||
lambdaGatherer.accept(method);
|
||||
changed = true;
|
||||
changes.compareAndSet(false, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return changes.get() ? ComputeFlags.SIMPLE_REWRITE : ComputeFlags.NO_REWRITE;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static List<AnnotationNode> unpack(final List<AnnotationNode> anns)
|
||||
{
|
||||
if (anns == null)
|
||||
{
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
List<AnnotationNode> unpacked = new ArrayList<>();
|
||||
|
||||
for (var annotationNode : anns)
|
||||
{
|
||||
if (Objects.equals(annotationNode.desc, ONLYINS))
|
||||
{
|
||||
unpacked.add(annotationNode);
|
||||
|
||||
if (annotationNode.values != null)
|
||||
{
|
||||
List<AnnotationNode> subNodes = (List<AnnotationNode>) annotationNode.values.get(annotationNode.values.indexOf("value") + 1);
|
||||
|
||||
if (subNodes != null)
|
||||
{
|
||||
unpacked.addAll(subNodes);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return changed ? ComputeFlags.SIMPLE_REWRITE : ComputeFlags.NO_REWRITE;
|
||||
return unpacked;
|
||||
}
|
||||
|
||||
private static record Target(String side, String intf) {
|
||||
static Target from(final AnnotationNode node) {
|
||||
var idx = node.values.indexOf("value");
|
||||
var value = (String[])node.values.get(idx + 1); // Enums are stored as [Type, Value]
|
||||
private static boolean remove(final List<AnnotationNode> anns, final String side)
|
||||
{
|
||||
var onlyIns = unpack(anns);
|
||||
|
||||
idx = node.values.indexOf("_interface");
|
||||
if (idx == -1)
|
||||
return new Target(value[1], null);
|
||||
for (var onlyIn : onlyIns)
|
||||
{
|
||||
if (!Objects.equals(onlyIn.desc, ONLYINS) || onlyIn.values.contains("_interface"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var intf = (Type)node.values.get(idx + 1);
|
||||
return new Target(value[1], intf.getInternalName());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unpack any OnlyIn or OnlyIns annotations to Target objects.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private static List<Target> unpack(final List<AnnotationNode> anns) {
|
||||
if (anns == null || anns.isEmpty())
|
||||
return Collections.emptyList();
|
||||
|
||||
var ret = new ArrayList<Target>();
|
||||
for (var node : anns) {
|
||||
if (ONLYIN.equals(node.desc))
|
||||
ret.add(Target.from(node));
|
||||
else if (ONLYINS.equals(node.desc)) {
|
||||
if (node.values == null || node.values.isEmpty())
|
||||
continue;
|
||||
|
||||
int idx = node.values.indexOf("value");
|
||||
if (idx == -1)
|
||||
continue;
|
||||
|
||||
var subNodes = (List<AnnotationNode>)node.values.get(idx + 1);
|
||||
if (subNodes != null) {
|
||||
for (var sub : subNodes)
|
||||
ret.add(Target.from(sub));
|
||||
}
|
||||
if (!Objects.equals(((String[])onlyIn.values.get(onlyIn.values.indexOf("value") + 1))[1], side))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
private static boolean remove(final List<Target> targets, final String side) {
|
||||
for (var target : targets) {
|
||||
if (target.intf == null && !side.equals(target.side))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean hasModAnnotation(final List<AnnotationNode> anns) {
|
||||
if (anns == null || anns.isEmpty())
|
||||
private static boolean hasOnlyInWithModAnnotation(final List<AnnotationNode> anns)
|
||||
{
|
||||
if (anns == null)
|
||||
{
|
||||
return false;
|
||||
|
||||
for (var ann : anns) {
|
||||
if (ann.desc.equals(MOD))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
var foundModAnnotation = false;
|
||||
var foundOnlyIn = false;
|
||||
|
||||
for (var ann : anns)
|
||||
{
|
||||
if (ann.desc.equals("Lnet/minecraftforge/fml/common/Mod;"))
|
||||
{
|
||||
foundModAnnotation = true;
|
||||
} else if (Objects.equals(ann.desc, ONLYIN) || Objects.equals(ann.desc, ONLYINS))
|
||||
{
|
||||
foundOnlyIn = true;
|
||||
}
|
||||
}
|
||||
|
||||
return foundModAnnotation && foundOnlyIn;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public Consumer<Dist> getExtension() {
|
||||
return s -> {
|
||||
public Consumer<Dist> getExtension()
|
||||
{
|
||||
return (s)-> {
|
||||
DIST = s.name();
|
||||
LOGGER.debug(DISTXFORM, "Configuring for Dist {}", DIST);
|
||||
};
|
||||
@@ -225,21 +254,23 @@ public class RuntimeDistCleaner implements ILaunchPluginService {
|
||||
}
|
||||
|
||||
public void accept(MethodNode method) {
|
||||
for (var insn : method.instructions) {
|
||||
if (insn.getType() == AbstractInsnNode.INVOKE_DYNAMIC_INSN)
|
||||
insn.accept(this);
|
||||
}
|
||||
Streams.stream(method.instructions.iterator()).
|
||||
filter(insnNode->insnNode.getType() == AbstractInsnNode.INVOKE_DYNAMIC_INSN).
|
||||
forEach(insnNode->insnNode.accept(this));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, Object... bsmArgs) {
|
||||
if (META_FACTORY.equals(bsm)) {
|
||||
var handle = (Handle)bsmArgs[1];
|
||||
dynamicLambdaHandles.add(handle);
|
||||
public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, Object... bsmArgs)
|
||||
{
|
||||
if (META_FACTORY.equals(bsm))
|
||||
{
|
||||
Handle dynamicLambdaHandle = (Handle) bsmArgs[1];
|
||||
dynamicLambdaHandles.add(dynamicLambdaHandle);
|
||||
}
|
||||
}
|
||||
|
||||
public List<Handle> getDynamicLambdaHandles() {
|
||||
public List<Handle> getDynamicLambdaHandles()
|
||||
{
|
||||
return dynamicLambdaHandles;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,17 +14,16 @@ import org.apache.commons.lang3.text.StrSubstitutor;
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public class StringSubstitutor {
|
||||
private static final Map<String, String> GLOBALS = Map.of(
|
||||
"mcVersion", FMLLoader.versionInfo().mcVersion(),
|
||||
"forgeVersion", FMLLoader.versionInfo().forgeVersion()
|
||||
);
|
||||
|
||||
public static String replace(final String in, final ModFile file) {
|
||||
return new StrSubstitutor(getStringLookup(file)).replace(in);
|
||||
}
|
||||
|
||||
private static StrLookup<String> getStringLookup(final ModFile file) {
|
||||
return new StrLookup<>() {
|
||||
var globals = Map.of(
|
||||
"mcVersion", FMLLoader.versionInfo().mcVersion(),
|
||||
"forgeVersion", FMLLoader.versionInfo().forgeVersion()
|
||||
);
|
||||
return new StrLookup<String>() {
|
||||
@Override
|
||||
public String lookup(String key) {
|
||||
var parts = key.split("\\.");
|
||||
@@ -33,7 +32,7 @@ public class StringSubstitutor {
|
||||
var pfx = parts[0];
|
||||
|
||||
if ("global".equals(pfx))
|
||||
return GLOBALS.get(parts[1]);
|
||||
return globals.get(parts[1]);
|
||||
else if ("file".equals(pfx) && file != null)
|
||||
return String.valueOf(file.getSubstitutionMap().get().get(parts[1]));
|
||||
|
||||
|
||||
@@ -47,12 +47,12 @@ public class UniqueModListBuilder
|
||||
|
||||
// Select the newest by artifact version sorting of non-unique files thus identified
|
||||
uniqueModList = modFilesByFirstId.entrySet().stream()
|
||||
.map(UniqueModListBuilder::selectNewestModInfo)
|
||||
.map(this::selectNewestModInfo)
|
||||
.toList();
|
||||
|
||||
// Select the newest by artifact version sorting of non-unique files thus identified
|
||||
uniqueLibListWithVersion = libFilesWithVersionByModuleName.entrySet().stream()
|
||||
.map(UniqueModListBuilder::selectNewestModInfo)
|
||||
.map(this::selectNewestModInfo)
|
||||
.toList();
|
||||
|
||||
// Transform to the full mod id list
|
||||
@@ -75,7 +75,7 @@ public class UniqueModListBuilder
|
||||
final List<String> dupedModErrors = modIds.values().stream()
|
||||
.filter(modInfos -> modInfos.size() > 1)
|
||||
.map(mods -> String.format("\tMod ID: '%s' from mod files: %s",
|
||||
mods.getFirst().getModId(),
|
||||
mods.get(0).getModId(),
|
||||
mods.stream()
|
||||
.map(modInfo -> modInfo.getOwningFile().getFile().getFileName()).collect(joining(", "))
|
||||
)).toList();
|
||||
@@ -91,8 +91,9 @@ public class UniqueModListBuilder
|
||||
final List<String> dupedLibErrors = versionedLibIds.values().stream()
|
||||
.filter(modFiles -> modFiles.size() > 1)
|
||||
.map(mods -> String.format("\tLibrary: '%s' from files: %s",
|
||||
getModId(mods.getFirst()),
|
||||
mods.stream().map(ModFile::getFileName).collect(joining(", "))
|
||||
getModId(mods.get(0)),
|
||||
mods.stream()
|
||||
.map(modFile -> modFile.getFileName()).collect(joining(", "))
|
||||
)).toList();
|
||||
|
||||
if (!dupedLibErrors.isEmpty()) {
|
||||
@@ -106,23 +107,25 @@ public class UniqueModListBuilder
|
||||
final Map<String, List<ModFile>> uniqueModFilesByFirstId = uniqueModList.stream()
|
||||
.collect(groupingBy(UniqueModListBuilder::getModId));
|
||||
|
||||
final List<ModFile> loadedList = new ArrayList<>(uniqueModList);
|
||||
final List<ModFile> loadedList = new ArrayList<>();
|
||||
loadedList.addAll(uniqueModList);
|
||||
loadedList.addAll(uniqueLibListWithVersion);
|
||||
|
||||
return new UniqueModListData(loadedList, uniqueModFilesByFirstId);
|
||||
}
|
||||
|
||||
private static ModFile selectNewestModInfo(Map.Entry<String, List<ModFile>> fullList) {
|
||||
private ModFile selectNewestModInfo(Map.Entry<String, List<ModFile>> fullList) {
|
||||
List<ModFile> modInfoList = fullList.getValue();
|
||||
if (modInfoList.size() > 1) {
|
||||
LOGGER.debug("Found {} mods for first modid {}, selecting most recent based on version data", modInfoList.size(), fullList.getKey());
|
||||
modInfoList.sort(Comparator.comparing(UniqueModListBuilder::getVersion).reversed());
|
||||
LOGGER.debug("Selected file {} for modid {} with version {}", modInfoList.getFirst().getFileName(), fullList.getKey(), getVersion(modInfoList.getFirst()));
|
||||
modInfoList.sort(Comparator.comparing(this::getVersion).reversed());
|
||||
LOGGER.debug("Selected file {} for modid {} with version {}", modInfoList.get(0).getFileName(), fullList.getKey(), this.getVersion(modInfoList.get(0)));
|
||||
}
|
||||
return modInfoList.get(0);
|
||||
}
|
||||
|
||||
private static ArtifactVersion getVersion(final ModFile mf) {
|
||||
private ArtifactVersion getVersion(final ModFile mf)
|
||||
{
|
||||
if (mf.getModFileInfo() == null || mf.getModInfos() == null || mf.getModInfos().isEmpty()) {
|
||||
return mf.getJarVersion();
|
||||
}
|
||||
@@ -135,7 +138,7 @@ public class UniqueModListBuilder
|
||||
return modFile.getSecureJar().name();
|
||||
}
|
||||
|
||||
return modFile.getModFileInfo().getMods().get(0).getModId();
|
||||
return modFile.getModFileInfo().moduleName();
|
||||
}
|
||||
|
||||
public record UniqueModListData(List<ModFile> modFiles, Map<String, List<ModFile>> modFilesByFirstId) {}
|
||||
|
||||
@@ -5,53 +5,33 @@
|
||||
|
||||
package net.minecraftforge.fml.loading;
|
||||
|
||||
import net.minecraftforge.forgespi.language.MavenVersionAdapter;
|
||||
import org.apache.maven.artifact.versioning.ArtifactVersion;
|
||||
import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
|
||||
import org.apache.maven.artifact.versioning.VersionRange;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.BiPredicate;
|
||||
|
||||
@ApiStatus.Internal // since 1.21.1, will be made non-public in a later MC version
|
||||
public class VersionSupportMatrix {
|
||||
private static final Map<String, List<ArtifactVersion>> OVERRIDE_VERSIONS;
|
||||
|
||||
private static final HashMap<String, List<ArtifactVersion>> overrideVersions = new HashMap<>();
|
||||
static {
|
||||
if ("1.21.1".equals(FMLLoader.versionInfo().mcVersion())) {
|
||||
OVERRIDE_VERSIONS = Map.ofEntries(
|
||||
// 1.21.1 is compatible with 1.21
|
||||
entry("languageloader.javafml", "51"),
|
||||
entry("mod.minecraft", "1.21"),
|
||||
entry("mod.forge", "51.0.33")
|
||||
);
|
||||
} else {
|
||||
OVERRIDE_VERSIONS = Collections.emptyMap();
|
||||
final ArtifactVersion version = new DefaultArtifactVersion(FMLLoader.versionInfo().mcVersion());
|
||||
if (MavenVersionAdapter.createFromVersionSpec("[1.19.2]").containsVersion(version)) {
|
||||
// 1.19.2 is Compatible with 1.19.1
|
||||
add("languageloader.javafml", "42");
|
||||
add("mod.minecraft", "1.19.1");
|
||||
add("mod.forge", "42.0.9");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #testVersionSupportMatrix(VersionRange, String, String)} instead, unwrapping your BiPredicate.
|
||||
*/
|
||||
@Deprecated(forRemoval = true, since = "1.21.1")
|
||||
public static boolean testVersionSupportMatrix(VersionRange declaredRange, String lookupId, String type, BiPredicate<String, VersionRange> standardLookup) {
|
||||
private static void add(String key, String value) {
|
||||
overrideVersions.computeIfAbsent(key, k -> new ArrayList<>()).add(new DefaultArtifactVersion(value));
|
||||
}
|
||||
public static <T> boolean testVersionSupportMatrix(VersionRange declaredRange, String lookupId, String type, BiPredicate<String, VersionRange> standardLookup) {
|
||||
if (standardLookup.test(lookupId, declaredRange)) return true;
|
||||
return testVersionSupportMatrix(declaredRange, lookupId, type);
|
||||
}
|
||||
|
||||
public static boolean testVersionSupportMatrix(VersionRange declaredRange, String lookupId, String type) {
|
||||
if (OVERRIDE_VERSIONS.isEmpty()) return false;
|
||||
List<ArtifactVersion> custom = OVERRIDE_VERSIONS.get(type + "." + lookupId);
|
||||
return custom != null && custom.stream().anyMatch(declaredRange::containsVersion);
|
||||
}
|
||||
|
||||
private static Map.Entry<String, List<ArtifactVersion>> entry(String typeAndLookupId, String declaredRange) {
|
||||
return Map.entry(typeAndLookupId, List.of(new DefaultArtifactVersion(declaredRange)));
|
||||
}
|
||||
|
||||
private static Map.Entry<String, List<ArtifactVersion>> entry(String typeAndLookupId, List<String> declaredRanges) {
|
||||
return Map.entry(typeAndLookupId, declaredRanges.stream().map(DefaultArtifactVersion::new).map(it -> (ArtifactVersion) it).toList());
|
||||
List<ArtifactVersion> custom = overrideVersions.get(type + "." + lookupId);
|
||||
return custom == null ? false : custom.stream().anyMatch(declaredRange::containsVersion);
|
||||
}
|
||||
}
|
||||
@@ -49,7 +49,10 @@ public class ForgeHighlight {
|
||||
LOGGER.warn("Failed to invoke initializeTerminal on TCA", e);
|
||||
}
|
||||
if (!TerminalConsoleAppender.isAnsiSupported() && Arrays.stream(options).noneMatch(s -> s.equals("disableAnsi=true"))) {
|
||||
options = new String[] { options[0], "disableAnsi=true" };
|
||||
List<String> optionList = new ArrayList<>();
|
||||
optionList.add(options[0]);
|
||||
optionList.add("disableAnsi=true");
|
||||
options = optionList.toArray(new String[0]);
|
||||
}
|
||||
return HighlightConverter.newInstance(config, options);
|
||||
}
|
||||
|
||||
+9
-70
@@ -6,7 +6,6 @@
|
||||
package net.minecraftforge.fml.loading.moddiscovery;
|
||||
|
||||
import com.mojang.logging.LogUtils;
|
||||
|
||||
import cpw.mods.jarhandling.JarMetadata;
|
||||
import cpw.mods.jarhandling.SecureJar;
|
||||
import net.minecraftforge.fml.loading.LogMarkers;
|
||||
@@ -23,25 +22,19 @@ import org.jetbrains.annotations.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.module.InvalidModuleDescriptorException;
|
||||
import java.lang.module.ModuleDescriptor;
|
||||
import java.lang.module.ModuleDescriptor.Version;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.jar.JarFile;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@ApiStatus.Internal
|
||||
public abstract class AbstractModProvider implements IModProvider {
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
protected static final String MODS_TOML = "META-INF/mods.toml";
|
||||
protected static final String MODULE_INFO = "module-info.class";
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
protected static final String MODS_TOML = "META-INF/mods.toml";
|
||||
|
||||
protected IModLocator.ModFileOrException createMod(Path path) {
|
||||
return createMod(path, false);
|
||||
@@ -49,18 +42,16 @@ public abstract class AbstractModProvider implements IModProvider {
|
||||
|
||||
@Nullable
|
||||
protected IModLocator.ModFileOrException createMod(Path path, boolean ignoreUnknown) {
|
||||
return createMod(path, ignoreUnknown, getDefaultJarModType());
|
||||
return createMod(path, false, getDefaultJarModType());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected IModLocator.ModFileOrException createMod(Path path, boolean ignoreUnknown, String defaultType) {
|
||||
var mjm = new ModJarMetadata();
|
||||
SecureJar sj = null;
|
||||
try {
|
||||
sj = SecureJar.from(jar -> loadMetaFromJar(jar, mjm), path);
|
||||
} catch (Throwable t) {
|
||||
return new IModLocator.ModFileOrException(null, new ModFileLoadingException("Failed to create secure jar for \"" + path + "\" - " + t.getMessage()));
|
||||
}
|
||||
var sj = SecureJar.from(
|
||||
jar -> jar.moduleDataProvider().findFile(MODS_TOML).isPresent() ? mjm : JarMetadata.from(jar, path),
|
||||
path
|
||||
);
|
||||
|
||||
IModFile mod;
|
||||
var type = sj.moduleDataProvider().getManifest().getMainAttributes().getValue(ModFile.TYPE);
|
||||
@@ -70,8 +61,6 @@ public abstract class AbstractModProvider implements IModProvider {
|
||||
if (sj.moduleDataProvider().findFile(MODS_TOML).isPresent()) {
|
||||
LOGGER.debug(LogMarkers.SCAN, "Found {} mod of type {}: {}", MODS_TOML, type, path);
|
||||
mod = new ModFile(sj, this, ModFileParser::modsTomlParser);
|
||||
// ModJarMetadata is only used when loading mods.toml mods as ModJarMetadata
|
||||
mjm.setModFile(mod);
|
||||
if (mod.getModFileInfo().getFileProperties().containsKey(ModFileInfo.NOT_A_FORGE_MOD_PROP)) {
|
||||
LOGGER.error(LogMarkers.SCAN, "Unable to load file \"{}\" because its mods.toml is requesting an invalid javafml loaderVersion (use \"*\" if you want to allow all versions) and is missing a forge modId dependency declaration (see the sample mods.toml in the MDK).", path);
|
||||
return new IModLocator.ModFileOrException(null, new ModFileLoadingException("File \"%s\" is not a Forge mod and cannot be loaded. Look for a Forge version of this mod or consider alternative mods.".formatted(mod.getFileName())));
|
||||
@@ -84,63 +73,14 @@ public abstract class AbstractModProvider implements IModProvider {
|
||||
} else
|
||||
return new IModLocator.ModFileOrException(null, new ModFileLoadingException("Invalid mod file found " + path));
|
||||
|
||||
mjm.setModFile(mod);
|
||||
return new IModLocator.ModFileOrException(mod, null);
|
||||
}
|
||||
|
||||
private static JarMetadata loadMetaFromJar(SecureJar jar, ModJarMetadata mjm) {
|
||||
var info = jar.moduleDataProvider().open(MODULE_INFO).orElse(null);
|
||||
if (info != null) {
|
||||
try {
|
||||
var desc = ModuleDescriptor.read(info, jar::getPackages);
|
||||
var all = new HashSet<>(jar.getPackages());
|
||||
all.removeAll(desc.packages());
|
||||
if (!all.isEmpty()) {
|
||||
var missing = all.stream().sorted().collect(Collectors.joining(", "));
|
||||
LOGGER.error("Invalid module-info, missing packages " + missing);
|
||||
throw new ModFileLoadingException("Invalid module-info, missing packages " + missing);
|
||||
} else {
|
||||
return new JarMetadata() {
|
||||
@Override
|
||||
public String name() {
|
||||
return desc.name();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String version() {
|
||||
return desc.version().map(Version::toString).or(desc::rawVersion).orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModuleDescriptor descriptor() {
|
||||
return desc;
|
||||
}
|
||||
};
|
||||
}
|
||||
} catch (InvalidModuleDescriptorException | IOException e) {
|
||||
LOGGER.error("Failed to parse " + jar.getPrimaryPath() + " module-info", e);
|
||||
throw new ModFileLoadingException("Invalid module-info: " + e.getMessage());
|
||||
} finally {
|
||||
try {
|
||||
info.close();
|
||||
} catch (IOException e) {}
|
||||
}
|
||||
}
|
||||
|
||||
if (jar.moduleDataProvider().findFile(MODS_TOML).isEmpty())
|
||||
return JarMetadata.from(jar, jar.getPrimaryPath());
|
||||
|
||||
return mjm;
|
||||
}
|
||||
|
||||
protected IModFileInfo manifestParser(final IModFile mod) {
|
||||
var mf = mod.getSecureJar().moduleDataProvider().getManifest().getMainAttributes();
|
||||
var license = mf.getValue("LICENSE");
|
||||
var dummy = new IConfigurable() {
|
||||
@Override
|
||||
public <T> Optional<T> getConfigElement(String key) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Optional<T> getConfigElement(String... key) {
|
||||
return Optional.empty();
|
||||
@@ -200,11 +140,10 @@ public abstract class AbstractModProvider implements IModProvider {
|
||||
}
|
||||
|
||||
private static final class Holder<T> {
|
||||
private T value;
|
||||
T value;
|
||||
}
|
||||
|
||||
private record DefaultModFileInfo(IModFile mod, String license, IConfigurable configurable) implements IModFileInfo, IConfigurable {
|
||||
@Override public <T> Optional<T> getConfigElement(final String string) { return Optional.empty(); }
|
||||
@Override public <T> Optional<T> getConfigElement(final String... strings) { return Optional.empty(); }
|
||||
@Override public List<? extends IConfigurable> getConfigList(final String... strings) { return null; }
|
||||
@Override public List<IModInfo> getMods() { return Collections.emptyList(); }
|
||||
|
||||
+5
-6
@@ -29,7 +29,6 @@ public class BackgroundScanHandler
|
||||
}
|
||||
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
private static final boolean DEBUG = LOGGER.isErrorEnabled(LogMarkers.SCAN);
|
||||
private final ExecutorService modContentScanner;
|
||||
private final List<ModFile> modFiles;
|
||||
private ScanStatus status;
|
||||
@@ -56,16 +55,16 @@ public class BackgroundScanHandler
|
||||
}
|
||||
status = ScanStatus.RUNNING;
|
||||
ImmediateWindowHandler.updateProgress("Scanning mod candidates");
|
||||
CompletableFuture<ModFileScanData> future = CompletableFuture.supplyAsync(file::compileContent, modContentScanner)
|
||||
.whenComplete(file::setScanResult);
|
||||
if (DEBUG) future = future.whenComplete((r, t) -> addCompletedFile(file, t));
|
||||
final CompletableFuture<ModFileScanData> future = CompletableFuture.supplyAsync(file::compileContent, modContentScanner)
|
||||
.whenComplete(file::setScanResult)
|
||||
.whenComplete((r,t)-> this.addCompletedFile(file,r,t));
|
||||
file.setFutureScanResult(future);
|
||||
}
|
||||
|
||||
private void addCompletedFile(final ModFile file, final Throwable throwable) {
|
||||
private void addCompletedFile(final ModFile file, final ModFileScanData modFileScanData, final Throwable throwable) {
|
||||
if (throwable != null) {
|
||||
status = ScanStatus.ERRORED;
|
||||
LOGGER.error(LogMarkers.SCAN, "An error occurred scanning file {}", file, throwable);
|
||||
LOGGER.error(LogMarkers.SCAN,"An error occurred scanning file {}", file, throwable);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+6
-5
@@ -18,9 +18,9 @@ import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.jar.Attributes;
|
||||
@@ -28,7 +28,7 @@ import java.util.jar.JarFile;
|
||||
import java.util.jar.Manifest;
|
||||
|
||||
@ApiStatus.Internal
|
||||
public final class ClasspathLocator extends AbstractModProvider implements IModLocator {
|
||||
public class ClasspathLocator extends AbstractModProvider implements IModLocator {
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
private static final Attributes.Name MOD_TYPE = new Attributes.Name("FMLModType");
|
||||
|
||||
@@ -80,11 +80,11 @@ public final class ClasspathLocator extends AbstractModProvider implements IModL
|
||||
return ret;
|
||||
}
|
||||
|
||||
private static List<URL> getUrls(ClassLoader cl, String resource) {
|
||||
private List<URL> getUrls(ClassLoader cl, String resource) {
|
||||
try {
|
||||
var lst = Collections.list(cl.getResources(resource));
|
||||
if (LOGGER.isDebugEnabled(LogMarkers.SCAN)) {
|
||||
lst.sort(Comparator.comparing(URL::toString));
|
||||
Collections.sort(lst, (a, b) -> a.toString().compareTo(b.toString()));
|
||||
LOGGER.debug(LogMarkers.SCAN, "Scanning Classloader: {} for {}", cl, resource);
|
||||
for (var url : lst)
|
||||
LOGGER.debug(LogMarkers.SCAN, "\t{}", url);
|
||||
@@ -104,7 +104,8 @@ public final class ClasspathLocator extends AbstractModProvider implements IModL
|
||||
len += 2;
|
||||
}
|
||||
str = str.substring(0, str.length() - len);
|
||||
return Path.of(URI.create(str));
|
||||
var path = Paths.get(URI.create(str));
|
||||
return path;
|
||||
}
|
||||
|
||||
private static Path getPathFromResource(ClassLoader cl, String resource) {
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@ import java.nio.file.Path;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
@ApiStatus.Internal
|
||||
public final class CoreModFile implements ICoreModFile {
|
||||
public class CoreModFile implements ICoreModFile {
|
||||
private final Path internalPath;
|
||||
private final ModFile file;
|
||||
private final String name;
|
||||
|
||||
+9
-9
@@ -6,6 +6,7 @@
|
||||
package net.minecraftforge.fml.loading.moddiscovery;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.mojang.logging.LogUtils;
|
||||
import net.minecraftforge.fml.loading.EarlyLoadingException;
|
||||
import net.minecraftforge.forgespi.language.IModInfo;
|
||||
@@ -25,7 +26,6 @@ import java.nio.file.FileSystem;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
@@ -51,7 +51,7 @@ public class JarInJarDependencyLocator extends AbstractModProvider implements ID
|
||||
|
||||
@Override
|
||||
public List<IModFile> scanMods(Iterable<IModFile> loadedMods) {
|
||||
final List<IModFile> sources = new ArrayList<>();
|
||||
final List<IModFile> sources = Lists.newArrayList();
|
||||
loadedMods.forEach(sources::add);
|
||||
|
||||
var dependenciesToLoad = JarSelector.detectAndSelect(
|
||||
@@ -102,34 +102,34 @@ public class JarInJarDependencyLocator extends AbstractModProvider implements ID
|
||||
protected EarlyLoadingException exception(Collection<JarSelector.ResolutionFailureInformation<IModFile>> failedDependencies) {
|
||||
final List<EarlyLoadingException.ExceptionData> errors = failedDependencies.stream()
|
||||
.filter(entry -> !entry.sources().isEmpty()) //Should never be the case, but just to be sure
|
||||
.map(JarInJarDependencyLocator::buildExceptionData)
|
||||
.map(this::buildExceptionData)
|
||||
.toList();
|
||||
|
||||
return new EarlyLoadingException(failedDependencies.size() + " Dependency restrictions were not met.", null, errors);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static EarlyLoadingException.ExceptionData buildExceptionData(JarSelector.ResolutionFailureInformation<IModFile> entry) {
|
||||
private EarlyLoadingException.ExceptionData buildExceptionData(JarSelector.ResolutionFailureInformation<IModFile> entry) {
|
||||
return new EarlyLoadingException.ExceptionData(
|
||||
getErrorTranslationKey(entry),
|
||||
entry.identifier().group() + ":" + entry.identifier().artifact(),
|
||||
entry.sources()
|
||||
.stream()
|
||||
.flatMap(JarInJarDependencyLocator::getModWithVersionRangeStream)
|
||||
.map(JarInJarDependencyLocator::formatError)
|
||||
.flatMap(this::getModWithVersionRangeStream)
|
||||
.map(this::formatError)
|
||||
.collect(Collectors.joining(", "))
|
||||
);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static String getErrorTranslationKey(JarSelector.ResolutionFailureInformation<IModFile> entry) {
|
||||
private String getErrorTranslationKey(JarSelector.ResolutionFailureInformation<IModFile> entry) {
|
||||
return entry.failureReason() == JarSelector.FailureReason.VERSION_RESOLUTION_FAILED ?
|
||||
"fml.dependencyloading.conflictingdependencies" :
|
||||
"fml.dependencyloading.mismatchedcontaineddependencies";
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static Stream<ModWithVersionRange> getModWithVersionRangeStream(JarSelector.SourceWithRequestedVersionRange<IModFile> file) {
|
||||
private Stream<ModWithVersionRange> getModWithVersionRangeStream(JarSelector.SourceWithRequestedVersionRange<IModFile> file) {
|
||||
return file.sources()
|
||||
.stream()
|
||||
.map(IModFile::getModFileInfo)
|
||||
@@ -152,7 +152,7 @@ public class JarInJarDependencyLocator extends AbstractModProvider implements ID
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static String formatError(ModWithVersionRange modWithVersionRange){
|
||||
private String formatError(ModWithVersionRange modWithVersionRange){
|
||||
return YELLOW + modWithVersionRange.modInfo().getModId() + RESET + " - " +
|
||||
RED + modWithVersionRange.versionRange().toString() + RESET + " - " +
|
||||
GREEN + modWithVersionRange.artifactVersion().toString() + RESET;
|
||||
|
||||
+8
-6
@@ -18,22 +18,24 @@ import java.util.List;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
@ApiStatus.Internal
|
||||
public final class MinecraftLocator extends AbstractModProvider implements IModLocator {
|
||||
public class MinecraftLocator extends AbstractModProvider implements IModLocator {
|
||||
@Override
|
||||
public List<IModLocator.ModFileOrException> scanMods() {
|
||||
var minecraft = FMLLoader.getLaunchHandler().getMinecraftPaths();
|
||||
var paths = minecraft.toArray(Path[]::new);
|
||||
|
||||
// Minecraft itself.
|
||||
var meta = new ModJarMetadata();
|
||||
var mcjar = SecureJar.from(jar -> meta, paths);
|
||||
var mc = ModFileFactory.FACTORY.build(mcjar, this, MinecraftLocator::buildMinecraftTOML);
|
||||
var mcjar = SecureJar.from(
|
||||
jar -> meta,
|
||||
minecraft.toArray(Path[]::new)
|
||||
);
|
||||
var mc = ModFileFactory.FACTORY.build(mcjar, this, this::buildMinecraftTOML);
|
||||
meta.setModFile(mc);
|
||||
|
||||
return List.of(new ModFileOrException(mc, null));
|
||||
}
|
||||
|
||||
private static IModFileInfo buildMinecraftTOML(final IModFile iModFile) {
|
||||
private IModFileInfo buildMinecraftTOML(final IModFile iModFile) {
|
||||
// We haven't changed this in years, and I can't be asked right now to special case this one file in the path.
|
||||
final var conf = Config.inMemory();
|
||||
conf.set("modLoader", "minecraft");
|
||||
@@ -50,7 +52,7 @@ public final class MinecraftLocator extends AbstractModProvider implements IModL
|
||||
conf.set("mods", List.of(mods));
|
||||
|
||||
var configWrapper = new NightConfigWrapper(conf);
|
||||
return new ModFileInfo((ModFile) iModFile, configWrapper, configWrapper::setFile);
|
||||
return new ModFileInfo((ModFile)iModFile, configWrapper, configWrapper::setFile, List.of());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+12
-13
@@ -6,6 +6,7 @@
|
||||
package net.minecraftforge.fml.loading.moddiscovery;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.mojang.logging.LogUtils;
|
||||
import cpw.mods.modlauncher.Launcher;
|
||||
import cpw.mods.modlauncher.api.IModuleLayerManager;
|
||||
@@ -27,7 +28,6 @@ import org.jetbrains.annotations.ApiStatus;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
@@ -49,19 +49,17 @@ public class ModDiscoverer {
|
||||
modLocators = ServiceLoader.load(moduleLayerManager.getLayer(IModuleLayerManager.Layer.SERVICE).orElseThrow(), IModLocator.class);
|
||||
dependencyLocators = ServiceLoader.load(moduleLayerManager.getLayer(IModuleLayerManager.Layer.SERVICE).orElseThrow(), IDependencyLocator.class);
|
||||
modLocatorList = ServiceLoaderUtils.streamServiceLoader(()-> modLocators, sce->LOGGER.error("Failed to load mod locator list", sce)).collect(Collectors.toList());
|
||||
for (IModLocator iModLocator : modLocatorList) {
|
||||
iModLocator.initArguments(arguments);
|
||||
}
|
||||
modLocatorList.forEach(l->l.initArguments(arguments));
|
||||
dependencyLocatorList = ServiceLoaderUtils.streamServiceLoader(()-> dependencyLocators, sce->LOGGER.error("Failed to load dependency locator list", sce)).collect(Collectors.toList());
|
||||
for (IDependencyLocator l : dependencyLocatorList) {
|
||||
l.initArguments(arguments);
|
||||
}
|
||||
dependencyLocatorList.forEach(l->l.initArguments(arguments));
|
||||
if (LOGGER.isDebugEnabled(LogMarkers.CORE))
|
||||
{
|
||||
LOGGER.debug(LogMarkers.CORE, "Found Mod Locators : {}", modLocatorList.stream()
|
||||
.map(modLocator -> "(%s:%s)".formatted(modLocator.name(),
|
||||
modLocator.getClass().getPackage().getImplementationVersion())).collect(Collectors.joining(",")));
|
||||
|
||||
}
|
||||
if (LOGGER.isDebugEnabled(LogMarkers.CORE))
|
||||
{
|
||||
LOGGER.debug(LogMarkers.CORE, "Found Dependency Locators : {}", dependencyLocatorList.stream()
|
||||
.map(dependencyLocator -> "(%s:%s)".formatted(dependencyLocator.name(),
|
||||
dependencyLocator.getClass().getPackage().getImplementationVersion())).collect(Collectors.joining(",")));
|
||||
@@ -128,7 +126,7 @@ public class ModDiscoverer {
|
||||
}
|
||||
|
||||
//First processing run of the mod list. Any duplicates will cause resolution failure and dependency loading will be skipped.
|
||||
Map<IModFile.Type, List<ModFile>> modFilesMap = new EnumMap<>(IModFile.Type.class);
|
||||
Map<IModFile.Type, List<ModFile>> modFilesMap = Maps.newHashMap();
|
||||
try {
|
||||
final UniqueModListBuilder modsUniqueListBuilder = new UniqueModListBuilder(loadedFiles);
|
||||
final UniqueModListBuilder.UniqueModListData uniqueModsData = modsUniqueListBuilder.buildUniqueList();
|
||||
@@ -136,7 +134,7 @@ public class ModDiscoverer {
|
||||
//Grab the temporary results.
|
||||
//This allows loading to continue to a base state, in case dependency loading fails.
|
||||
modFilesMap = uniqueModsData.modFiles().stream()
|
||||
.collect(Collectors.groupingBy(IModFile::getType, () -> new EnumMap<>(IModFile.Type.class), Collectors.toList()));
|
||||
.collect(Collectors.groupingBy(IModFile::getType));
|
||||
loadedFiles = uniqueModsData.modFiles();
|
||||
}
|
||||
catch (EarlyLoadingException exception) {
|
||||
@@ -173,11 +171,11 @@ public class ModDiscoverer {
|
||||
|
||||
//We now only need the mod files map, not the list.
|
||||
modFilesMap = uniqueModsAndDependenciesData.modFiles().stream()
|
||||
.collect(Collectors.groupingBy(IModFile::getType, () -> new EnumMap<>(IModFile.Type.class), Collectors.toList()));
|
||||
.collect(Collectors.groupingBy(IModFile::getType));
|
||||
} catch (EarlyLoadingException exception) {
|
||||
LOGGER.error(LogMarkers.SCAN, "Failed to build unique mod list after dependency discovery.", exception);
|
||||
discoveryErrorData.addAll(exception.getAllData());
|
||||
modFilesMap = loadedFiles.stream().collect(Collectors.groupingBy(IModFile::getType, () -> new EnumMap<>(IModFile.Type.class), Collectors.toList()));
|
||||
modFilesMap = loadedFiles.stream().collect(Collectors.groupingBy(IModFile::getType));
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -192,7 +190,8 @@ public class ModDiscoverer {
|
||||
return validator;
|
||||
}
|
||||
|
||||
private static void handleLocatedFiles(final List<ModFile> loadedFiles, final List<IModFile> locatedFiles) {
|
||||
private void handleLocatedFiles(final List<ModFile> loadedFiles, final List<IModFile> locatedFiles)
|
||||
{
|
||||
var locatedModFiles = locatedFiles.stream().filter(ModFile.class::isInstance).map(ModFile.class::cast).toList();
|
||||
for (IModFile mf : locatedModFiles) {
|
||||
LOGGER.info(LogMarkers.SCAN, "Found mod file {} of type {} with provider {}", mf.getFileName(), mf.getType(), mf.getProvider());
|
||||
|
||||
@@ -45,6 +45,7 @@ public class ModFile implements IModFile {
|
||||
private Throwable scanError;
|
||||
private final SecureJar jar;
|
||||
private final Type modFileType;
|
||||
private final Manifest manifest;
|
||||
private final IModProvider provider;
|
||||
private IModFileInfo modFileInfo;
|
||||
private ModFileScanData fileModFileScanData;
|
||||
@@ -64,7 +65,7 @@ public class ModFile implements IModFile {
|
||||
this.jar = jar;
|
||||
this.parser = parser;
|
||||
|
||||
var manifest = this.jar.moduleDataProvider().getManifest();
|
||||
manifest = this.jar.moduleDataProvider().getManifest();
|
||||
modFileType = Type.valueOf(type);
|
||||
jarVersion = Optional.ofNullable(manifest.getMainAttributes().getValue(Attributes.Name.IMPLEMENTATION_VERSION)).orElse("0.0NONE");
|
||||
this.modFileInfo = ModFileParser.readModList(this, this.parser);
|
||||
|
||||
+69
-92
@@ -7,6 +7,7 @@ package net.minecraftforge.fml.loading.moddiscovery;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.mojang.logging.LogUtils;
|
||||
import cpw.mods.modlauncher.api.LamdbaExceptionUtils;
|
||||
import net.minecraftforge.fml.loading.LogMarkers;
|
||||
import net.minecraftforge.fml.loading.StringUtils;
|
||||
import net.minecraftforge.forgespi.language.IConfigurable;
|
||||
@@ -17,15 +18,17 @@ import org.jetbrains.annotations.ApiStatus;
|
||||
import org.slf4j.Logger;
|
||||
import javax.security.auth.x500.X500Principal;
|
||||
import java.net.URL;
|
||||
import java.security.CodeSigner;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.NoSuchProviderException;
|
||||
import java.security.SignatureException;
|
||||
import java.security.cert.CertificateEncodingException;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
@@ -34,8 +37,10 @@ import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class ModFileInfo implements IModFileInfo, IConfigurable {
|
||||
public class ModFileInfo implements IModFileInfo, IConfigurable
|
||||
{
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
private static final String MAYBE_NOT_JAVAFML_VER = "[2,)";
|
||||
private static final String JAVAFML = "javafml";
|
||||
@@ -55,7 +60,8 @@ public class ModFileInfo implements IModFileInfo, IConfigurable {
|
||||
private final String license;
|
||||
private final List<String> usesServices;
|
||||
|
||||
ModFileInfo(final ModFile modFile, final IConfigurable config, Consumer<IModFileInfo> configFileConsumer) {
|
||||
ModFileInfo(final ModFile modFile, final IConfigurable config, Consumer<IModFileInfo> configFileConsumer)
|
||||
{
|
||||
this.modFile = modFile;
|
||||
this.config = config;
|
||||
configFileConsumer.accept(this);
|
||||
@@ -80,6 +86,10 @@ public class ModFileInfo implements IModFileInfo, IConfigurable {
|
||||
.orElse(false);
|
||||
this.clientSideOnly = config.<Boolean>getConfigElement("clientSideOnly")
|
||||
.orElse(false);
|
||||
if (this.clientSideOnly) {
|
||||
// if clientSideOnly, then this is definitely a Forge mod
|
||||
maybeNotAForgeMod = false;
|
||||
}
|
||||
this.showAsDataPack = config.<Boolean>getConfigElement("showAsDataPack")
|
||||
.orElse(false);
|
||||
this.usesServices = config.<List<String>>getConfigElement("services")
|
||||
@@ -88,10 +98,6 @@ public class ModFileInfo implements IModFileInfo, IConfigurable {
|
||||
.map(StringUtils::toURL)
|
||||
.orElse(null);
|
||||
|
||||
// if clientSideOnly, then this is definitely a Forge mod
|
||||
if (this.clientSideOnly)
|
||||
maybeNotAForgeMod = false;
|
||||
|
||||
if (maybeNotAForgeMod) {
|
||||
// mark this file as not a Forge mod. Note: this marker may be removed later based on the mod dependencies
|
||||
this.properties = config.<Map<String, Object>>getConfigElement("properties")
|
||||
@@ -104,14 +110,15 @@ public class ModFileInfo implements IModFileInfo, IConfigurable {
|
||||
this.modFile.setFileProperties(this.properties);
|
||||
|
||||
final List<? extends IConfigurable> modConfigs = config.getConfigList("mods");
|
||||
if (modConfigs == null || modConfigs.isEmpty())
|
||||
if (modConfigs.isEmpty())
|
||||
{
|
||||
throw new InvalidModFileException("Missing mods list", this);
|
||||
|
||||
}
|
||||
this.mods = modConfigs.stream()
|
||||
.map(mi -> (IModInfo) ModInfo.of(this, mi))
|
||||
.map(mi-> (IModInfo)new ModInfo(this, mi))
|
||||
.toList();
|
||||
|
||||
if (LOGGER.isDebugEnabled(LogMarkers.LOADING)) {
|
||||
if (LOGGER.isDebugEnabled(LogMarkers.LOADING))
|
||||
{
|
||||
LOGGER.debug(LogMarkers.LOADING, "Found valid mod file {} with {} mods - versions {}",
|
||||
this.modFile.getFileName(),
|
||||
this.mods.stream().map(IModInfo::getModId).collect(Collectors.joining(",", "{", "}")),
|
||||
@@ -125,11 +132,13 @@ public class ModFileInfo implements IModFileInfo, IConfigurable {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<IModInfo> getMods() {
|
||||
public List<IModInfo> getMods()
|
||||
{
|
||||
return mods;
|
||||
}
|
||||
|
||||
public ModFile getFile() {
|
||||
public ModFile getFile()
|
||||
{
|
||||
return this.modFile;
|
||||
}
|
||||
|
||||
@@ -139,42 +148,44 @@ public class ModFileInfo implements IModFileInfo, IConfigurable {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getFileProperties() {
|
||||
public Map<String, Object> getFileProperties()
|
||||
{
|
||||
return this.properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean showAsResourcePack() {
|
||||
public boolean showAsResourcePack()
|
||||
{
|
||||
return this.showAsResourcePack;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isClientSideOnly() {
|
||||
public boolean isClientSideOnly()
|
||||
{
|
||||
return this.clientSideOnly;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean showAsDataPack() {
|
||||
public boolean showAsDataPack()
|
||||
{
|
||||
return this.showAsDataPack;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Optional<T> getConfigElement(final String key) {
|
||||
public <T> Optional<T> getConfigElement(final String... key)
|
||||
{
|
||||
return this.config.getConfigElement(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Optional<T> getConfigElement(final String... key) {
|
||||
return this.config.getConfigElement(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<? extends IConfigurable> getConfigList(final String... key) {
|
||||
public List<? extends IConfigurable> getConfigList(final String... key)
|
||||
{
|
||||
return this.config.getConfigList(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLicense() {
|
||||
public String getLicense()
|
||||
{
|
||||
return license;
|
||||
}
|
||||
|
||||
@@ -183,83 +194,54 @@ public class ModFileInfo implements IModFileInfo, IConfigurable {
|
||||
return this;
|
||||
}
|
||||
|
||||
public URL getIssueURL() {
|
||||
public URL getIssueURL()
|
||||
{
|
||||
return issueURL;
|
||||
}
|
||||
|
||||
public boolean missingLicense() {
|
||||
public boolean missingLicense()
|
||||
{
|
||||
return Strings.isNullOrEmpty(license);
|
||||
}
|
||||
|
||||
private static final char[] HEX = "0123456789ABCDEF".toCharArray();
|
||||
public Optional<String> getCodeSigningFingerprint() {
|
||||
var signers = this.modFile.getSecureJar().getManifestSigners();
|
||||
if (signers == null)
|
||||
return Optional.empty();
|
||||
|
||||
for (var signer : signers) {
|
||||
for (var cert : signer.getSignerCertPath().getCertificates()) {
|
||||
try {
|
||||
var encoded = cert.getEncoded();
|
||||
var sha256 = MessageDigest.getInstance("SHA-256");
|
||||
var digest = sha256.digest(encoded);
|
||||
var buf = new StringBuilder(digest.length * 3);
|
||||
for (int x = 0; x < digest.length - 1; x++) {
|
||||
buf.append(HEX[(digest[x] & 0xF0) >> 4]);
|
||||
buf.append(HEX[(digest[x] & 0x0F) ]);
|
||||
buf.append(':');
|
||||
}
|
||||
buf.append(HEX[(digest[digest.length - 1] & 0xF0) >> 4]);
|
||||
buf.append(HEX[(digest[digest.length - 1] & 0x0F) ]);
|
||||
|
||||
// We only care about the first. Should we list them all?
|
||||
return Optional.of(buf.toString());
|
||||
} catch (CertificateEncodingException | NoSuchAlgorithmException e) {
|
||||
sneak(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Optional.empty();
|
||||
|
||||
return (signers == null ? Stream.<CodeSigner>of() : Arrays.stream(signers))
|
||||
.flatMap(csa->csa.getSignerCertPath().getCertificates().stream())
|
||||
.findFirst()
|
||||
.map(LamdbaExceptionUtils.rethrowFunction(Certificate::getEncoded))
|
||||
.map(bytes->LamdbaExceptionUtils.uncheck(()->MessageDigest.getInstance("SHA-256")).digest(bytes))
|
||||
.map(StringUtils::binToHex)
|
||||
.map(str-> String.join(":", str.split("(?<=\\G.{2})")));
|
||||
}
|
||||
|
||||
public Optional<String> getTrustData() {
|
||||
var signers = this.modFile.getSecureJar().getManifestSigners();
|
||||
if (signers == null)
|
||||
return Optional.empty();
|
||||
|
||||
for (var signer : signers) {
|
||||
for (var cert : signer.getSignerCertPath().getCertificates()) {
|
||||
if (!(cert instanceof X509Certificate x509))
|
||||
continue;
|
||||
|
||||
var sb = new StringBuilder();
|
||||
sb.append(x509.getSubjectX500Principal().getName(X500Principal.RFC2253).split(",")[0]);
|
||||
|
||||
var selfSigned = false;
|
||||
try {
|
||||
x509.verify(x509.getPublicKey());
|
||||
selfSigned = true;
|
||||
} catch (CertificateException | NoSuchAlgorithmException | InvalidKeyException | SignatureException | NoSuchProviderException e) {
|
||||
// not self signed
|
||||
}
|
||||
|
||||
if (selfSigned)
|
||||
return Arrays.stream(this.modFile.getSecureJar().getManifestSigners())
|
||||
.flatMap(csa->csa.getSignerCertPath().getCertificates().stream())
|
||||
.findFirst()
|
||||
.map(X509Certificate.class::cast)
|
||||
.map(c->{
|
||||
StringBuffer sb = new StringBuffer();
|
||||
sb.append(c.getSubjectX500Principal().getName(X500Principal.RFC2253).split(",")[0]);
|
||||
boolean selfSigned = false;
|
||||
try {
|
||||
c.verify(c.getPublicKey());
|
||||
selfSigned = true;
|
||||
} catch (CertificateException | NoSuchAlgorithmException | InvalidKeyException | SignatureException | NoSuchProviderException e) {
|
||||
// not self signed
|
||||
}
|
||||
if (selfSigned) {
|
||||
sb.append(" self-signed");
|
||||
else
|
||||
sb.append(" signed by ").append(x509.getIssuerX500Principal().getName(X500Principal.RFC2253).split(",")[0]);
|
||||
|
||||
return Optional.of(sb.toString());
|
||||
}
|
||||
}
|
||||
|
||||
return Optional.empty();
|
||||
} else {
|
||||
sb.append(" signed by ").append(c.getIssuerX500Principal().getName(X500Principal.RFC2253).split(",")[0]);
|
||||
};
|
||||
return sb.toString();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public String moduleName() {
|
||||
return this.modFile.getSecureJar().name();
|
||||
return getMods().get(0).getModId();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -271,9 +253,4 @@ public class ModFileInfo implements IModFileInfo, IConfigurable {
|
||||
public List<String> usesServices() {
|
||||
return usesServices;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <E extends Throwable, R> R sneak(Throwable e) throws E {
|
||||
throw (E)e;
|
||||
}
|
||||
}
|
||||
+199
-154
@@ -19,189 +19,202 @@ import org.jetbrains.annotations.ApiStatus;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@ApiStatus.Internal
|
||||
public record ModInfo(
|
||||
ModFileInfo getOwningFile,
|
||||
IConfigurable getConfig,
|
||||
|
||||
String getModId,
|
||||
String getNamespace,
|
||||
ArtifactVersion getVersion,
|
||||
|
||||
String getDisplayName,
|
||||
String getDescription,
|
||||
Optional<String> getLogoFile,
|
||||
boolean getLogoBlur,
|
||||
Optional<URL> getUpdateURL,
|
||||
Optional<URL> getModURL,
|
||||
|
||||
Holder<List<? extends ModVersion>> dependencies,
|
||||
Holder<List<ForgeFeature.Bound>> forgeFeatures,
|
||||
Map<String, Object> getModProperties
|
||||
) implements IModInfo, IConfigurable {
|
||||
public class ModInfo implements IModInfo, IConfigurable {
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
private static final DefaultArtifactVersion DEFAULT_VERSION = new DefaultArtifactVersion("1");
|
||||
private static final Pattern VALID_MODID = Pattern.compile("^[a-z][a-z0-9_]{1,63}$");
|
||||
private static final Pattern VALID_NAMESPACE = Pattern.compile("^[a-z][a-z0-9_.-]{1,63}$");
|
||||
private static final Pattern VALID_VERSION = Pattern.compile("^\\d+.*");
|
||||
|
||||
public static ModInfo of(ModFileInfo owningFile, IConfigurable config) {
|
||||
private final ModFileInfo owningFile;
|
||||
private final String modId;
|
||||
private final String namespace;
|
||||
private final ArtifactVersion version;
|
||||
private final String displayName;
|
||||
private final String description;
|
||||
private final Optional<String> logoFile;
|
||||
private final boolean logoBlur;
|
||||
private final Optional<URL> updateJSONURL;
|
||||
private final List<? extends IModInfo.ModVersion> dependencies;
|
||||
|
||||
private final List<ForgeFeature.Bound> features;
|
||||
private final Map<String,Object> properties;
|
||||
private final IConfigurable config;
|
||||
private final Optional<URL> modUrl;
|
||||
|
||||
public ModInfo(final ModFileInfo owningFile, final IConfigurable config)
|
||||
{
|
||||
Optional<ModFileInfo> ownFile = Optional.ofNullable(owningFile);
|
||||
this.owningFile = owningFile;
|
||||
this.config = config;
|
||||
// These are sourced from the mod specific [[mod]] entry
|
||||
String modId = config.<String>getConfigElement("modId").orElse(null);
|
||||
if (modId == null)
|
||||
throw new InvalidModFileException("Missing modId", owningFile);
|
||||
|
||||
this.modId = config.<String>getConfigElement("modId")
|
||||
.orElseThrow(() -> new InvalidModFileException("Missing modId", owningFile));
|
||||
// verify we have a valid modid
|
||||
if (!VALID_MODID.matcher(modId).matches()) {
|
||||
LOGGER.error(LogUtils.FATAL_MARKER, "Invalid modId found in file {} - {} does not match the standard: {}", owningFile.getFile().getFilePath(), modId, VALID_MODID.pattern());
|
||||
throw new InvalidModFileException("Invalid modId found : " + modId, owningFile);
|
||||
if (!VALID_MODID.matcher(this.modId).matches()) {
|
||||
LOGGER.error(LogUtils.FATAL_MARKER, "Invalid modId found in file {} - {} does not match the standard: {}", this.owningFile.getFile().getFilePath(), this.modId, VALID_MODID.pattern());
|
||||
throw new InvalidModFileException("Invalid modId found : " + this.modId, owningFile);
|
||||
}
|
||||
|
||||
String namespace = config.<String>getConfigElement("namespace")
|
||||
.orElse(modId);
|
||||
|
||||
this.namespace = config.<String>getConfigElement("namespace")
|
||||
.orElse(this.modId);
|
||||
// verify our namespace is valid
|
||||
if (!VALID_NAMESPACE.matcher(namespace).matches()) {
|
||||
LOGGER.error(LogUtils.FATAL_MARKER, "Invalid override namespace found in file {} - {} does not match the standard: {}", owningFile.getFile().getFilePath(), namespace, VALID_NAMESPACE.pattern());
|
||||
throw new InvalidModFileException("Invalid override namespace found : " + namespace, owningFile);
|
||||
if (!VALID_NAMESPACE.matcher(this.namespace).matches()) {
|
||||
LOGGER.error(LogUtils.FATAL_MARKER, "Invalid override namespace found in file {} - {} does not match the standard: {}", this.owningFile.getFile().getFilePath(), this.namespace, VALID_NAMESPACE.pattern());
|
||||
throw new InvalidModFileException("Invalid override namespace found : " + this.namespace, owningFile);
|
||||
}
|
||||
|
||||
ArtifactVersion version = config.<String>getConfigElement("version")
|
||||
.map(s -> StringSubstitutor.replace(s, owningFile.getFile()))
|
||||
this.version = config.<String>getConfigElement("version")
|
||||
.map(s -> StringSubstitutor.replace(s, ownFile.map(ModFileInfo::getFile).orElse(null)))
|
||||
.map(DefaultArtifactVersion::new)
|
||||
.orElse(DEFAULT_VERSION);
|
||||
|
||||
// verify we have a valid mod version
|
||||
if (!VALID_VERSION.matcher(version.toString()).matches())
|
||||
throw new InvalidModFileException("Illegal version number specified " + version, owningFile);
|
||||
|
||||
if (!VALID_VERSION.matcher(this.version.toString()).matches()) {
|
||||
throw new InvalidModFileException("Illegal version number specified "+this.version, this.getOwningFile());
|
||||
}
|
||||
// The remaining properties all default to sensible values and are not essential
|
||||
String displayName = config.<String>getConfigElement("displayName")
|
||||
.orElse(modId);
|
||||
|
||||
String description = config.<String>getConfigElement("description")
|
||||
this.displayName = config.<String>getConfigElement("displayName")
|
||||
.orElse(this.modId);
|
||||
this.description = config.<String>getConfigElement("description")
|
||||
.orElse("MISSING DESCRIPTION")
|
||||
.replace("\r\n", "\n").stripIndent();
|
||||
|
||||
Optional<String> logoFile = Optional.ofNullable(
|
||||
config.<String>getConfigElement("logoFile")
|
||||
.orElseGet(() -> owningFile.<String>getConfigElement("logoFile").orElse(null))
|
||||
);
|
||||
|
||||
Boolean logoBlur = config.<Boolean>getConfigElement("logoBlur").orElse(null);
|
||||
if (logoBlur == null)
|
||||
logoBlur = owningFile.<Boolean>getConfigElement("logoBlur").orElse(true);
|
||||
|
||||
Optional<URL> updateJSONURL = config.<String>getConfigElement("updateJSONURL")
|
||||
this.logoFile = Optional.ofNullable(config.<String>getConfigElement("logoFile")
|
||||
.orElseGet(() -> ownFile.flatMap(mf -> mf.<String>getConfigElement("logoFile"))
|
||||
.orElse(null)));
|
||||
this.logoBlur = config.<Boolean>getConfigElement("logoBlur")
|
||||
.orElseGet(() -> ownFile.flatMap(f -> f.<Boolean>getConfigElement("logoBlur"))
|
||||
.orElse(true));
|
||||
this.updateJSONURL = config.<String>getConfigElement("updateJSONURL")
|
||||
.map(StringUtils::toURL);
|
||||
|
||||
Optional<URL> modUrl = config.<String>getConfigElement("modUrl")
|
||||
this.modUrl = config.<String>getConfigElement("modUrl")
|
||||
.map(StringUtils::toURL);
|
||||
|
||||
// dependencies and features are done after the constructor as they need to reference the ModInfo we are creating
|
||||
List<? extends ModVersion> dependencies = Collections.emptyList();
|
||||
List<ForgeFeature.Bound> forgeFeatures = Collections.emptyList();
|
||||
|
||||
Map<String, Object> modProperties = owningFile.<Map<String, Object>>getConfigElement("modproperties", modId)
|
||||
.map(Collections::unmodifiableMap)
|
||||
// These are sourced from the file rather than the mod-specific block, but with a modid tag
|
||||
this.dependencies = ownFile.map(mfi -> mfi.getConfigList("dependencies", this.modId))
|
||||
.orElse(Collections.emptyList())
|
||||
.stream()
|
||||
.map(dep -> new ModVersion(this, dep))
|
||||
.toList();
|
||||
this.features = ownFile.flatMap(mfi -> mfi.<Map<String, Object>>getConfigElement("features", this.modId))
|
||||
.stream()
|
||||
.flatMap(m->m.entrySet().stream())
|
||||
.map(this::makeBound)
|
||||
.toList();
|
||||
this.properties = ownFile.flatMap(mfi -> mfi.<Map<String, Object>>getConfigElement("modproperties", this.modId))
|
||||
.orElse(Collections.emptyMap());
|
||||
|
||||
return new ModInfo(
|
||||
owningFile, config,
|
||||
modId, namespace, version,
|
||||
displayName, description, logoFile, logoBlur, updateJSONURL, modUrl,
|
||||
new Holder<>(dependencies), new Holder<>(forgeFeatures), modProperties
|
||||
).setupDependencies().setupForgeFeatures();
|
||||
}
|
||||
|
||||
private ModInfo setupDependencies() {
|
||||
var deps = getOwningFile.getConfigList("dependencies", getModId);
|
||||
if (deps == null || deps.isEmpty()) {
|
||||
dependencies.value = Collections.emptyList();
|
||||
return this;
|
||||
}
|
||||
|
||||
var tmp = new ModVersion[deps.size()];
|
||||
for (int i = 0; i < deps.size(); i++) {
|
||||
tmp[i] = ModVersion.of(this, deps.get(i));
|
||||
}
|
||||
dependencies.value = List.of(tmp);
|
||||
return this;
|
||||
}
|
||||
|
||||
private ModInfo setupForgeFeatures() {
|
||||
var feats = getOwningFile.<Map<String, Object>>getConfigElement("features", getModId).orElse(null);
|
||||
if (feats == null) {
|
||||
forgeFeatures.value = Collections.emptyList();
|
||||
return this;
|
||||
}
|
||||
|
||||
var tmp = new ArrayList<ForgeFeature.Bound>();
|
||||
for (var entry : feats.entrySet()) {
|
||||
if (!(entry.getValue() instanceof String val))
|
||||
throw new InvalidModFileException("Invalid feature bound {" + entry.getValue() + "} for key {" + entry.getKey() + "} only strings are accepted", getOwningFile);
|
||||
tmp.add(new ForgeFeature.Bound(entry.getKey(), val, this));
|
||||
}
|
||||
forgeFeatures.value = List.copyOf(tmp);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Optional<T> getConfigElement(String key) {
|
||||
return getConfig.getConfigElement(key);
|
||||
public ModFileInfo getOwningFile() {
|
||||
return owningFile;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Optional<T> getConfigElement(String... key) {
|
||||
return getConfig.getConfigElement(key);
|
||||
public String getModId() {
|
||||
return modId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<? extends IConfigurable> getConfigList(String... key) {
|
||||
return null;
|
||||
public String getDisplayName()
|
||||
{
|
||||
return this.displayName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription()
|
||||
{
|
||||
return this.description;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArtifactVersion getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<? extends IModInfo.ModVersion> getDependencies() {
|
||||
return dependencies.value;
|
||||
return this.dependencies;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNamespace() {
|
||||
return this.namespace;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getModProperties() {
|
||||
return this.properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<URL> getUpdateURL() {
|
||||
return this.updateJSONURL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<String> getLogoFile()
|
||||
{
|
||||
return this.logoFile;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getLogoBlur()
|
||||
{
|
||||
return this.logoBlur;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IConfigurable getConfig() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<? extends ForgeFeature.Bound> getForgeFeatures() {
|
||||
return forgeFeatures.value;
|
||||
return this.features;
|
||||
}
|
||||
|
||||
private static final class Holder<T> {
|
||||
private T value;
|
||||
@Override
|
||||
public <T> Optional<T> getConfigElement(final String... key) {
|
||||
return this.config.getConfigElement(key);
|
||||
}
|
||||
|
||||
public Holder(T value) {
|
||||
this.value = value;
|
||||
@Override
|
||||
public List<? extends IConfigurable> getConfigList(final String... key) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<URL> getModURL() {
|
||||
return modUrl;
|
||||
}
|
||||
|
||||
private ForgeFeature.Bound makeBound(Map.Entry<String, Object> e) {
|
||||
if (e.getValue() instanceof String val) {
|
||||
return new ForgeFeature.Bound(e.getKey(), val, this);
|
||||
} else {
|
||||
throw new InvalidModFileException("Invalid feature bound {" + e.getValue() + "} for key {" + e.getKey() + "} only strings are accepted", this.owningFile);
|
||||
}
|
||||
}
|
||||
|
||||
private record ModVersion(
|
||||
Holder<IModInfo> owner,
|
||||
class ModVersion implements net.minecraftforge.forgespi.language.IModInfo.ModVersion {
|
||||
private IModInfo owner;
|
||||
private final String modId;
|
||||
private final VersionRange versionRange;
|
||||
private final boolean mandatory;
|
||||
private final Ordering ordering;
|
||||
private final DependencySide side;
|
||||
private final Optional<URL> referralUrl;
|
||||
|
||||
String getModId,
|
||||
VersionRange getVersionRange,
|
||||
boolean isMandatory,
|
||||
Ordering getOrdering,
|
||||
DependencySide getSide,
|
||||
Optional<URL> getReferralURL
|
||||
) implements IModInfo.ModVersion {
|
||||
public static ModVersion of(IModInfo owner, IConfigurable config) {
|
||||
var modId = config.<String>getConfigElement("modId").orElse(null);
|
||||
if (modId == null)
|
||||
throw new InvalidModFileException("Missing required field modid in dependency", owner.getOwningFile());
|
||||
|
||||
if (modId.equals("forge")) {
|
||||
public ModVersion(final IModInfo owner, final IConfigurable config) {
|
||||
this.owner = owner;
|
||||
this.modId = config.<String>getConfigElement("modId")
|
||||
.orElseThrow(()->new InvalidModFileException("Missing required field modid in dependency", getOwningFile()));
|
||||
if (this.modId.equals("forge")) {
|
||||
var fileProps = owner.getOwningFile().getFileProperties();
|
||||
// Checking containsKey to avoid a possible exception if the property is not present (due to Collections.emptyMap())
|
||||
if (!fileProps.isEmpty() && fileProps.containsKey(ModFileInfo.NOT_A_FORGE_MOD_PROP)) {
|
||||
@@ -210,42 +223,74 @@ public record ModInfo(
|
||||
fileProps.remove(ModFileInfo.NOT_A_FORGE_MOD_PROP);
|
||||
}
|
||||
}
|
||||
|
||||
boolean mandatory;
|
||||
var mandatoryValue = config.<Boolean>getConfigElement("mandatory");
|
||||
if (mandatoryValue.isPresent())
|
||||
mandatory = mandatoryValue.get();
|
||||
else if (owner.getOwningFile().getFileProperties().containsKey(ModFileInfo.NOT_A_FORGE_MOD_PROP))
|
||||
mandatory = true;
|
||||
else
|
||||
throw new InvalidModFileException("Missing required field mandatory in dependency", owner.getOwningFile());
|
||||
|
||||
var versionRange = config.<String>getConfigElement("versionRange")
|
||||
var mandatory = config.<Boolean>getConfigElement("mandatory");
|
||||
if (mandatory.isPresent()) {
|
||||
this.mandatory = mandatory.get();
|
||||
} else if (owner.getOwningFile().getFileProperties().containsKey(ModFileInfo.NOT_A_FORGE_MOD_PROP)) {
|
||||
this.mandatory = true;
|
||||
} else {
|
||||
throw new InvalidModFileException("Missing required field mandatory in dependency", getOwningFile());
|
||||
}
|
||||
this.versionRange = config.<String>getConfigElement("versionRange")
|
||||
.map(MavenVersionAdapter::createFromVersionSpec)
|
||||
.orElse(UNBOUNDED);
|
||||
var ordering = config.<String>getConfigElement("ordering")
|
||||
this.ordering = config.<String>getConfigElement("ordering")
|
||||
.map(Ordering::valueOf)
|
||||
.orElse(Ordering.NONE);
|
||||
var side = config.<String>getConfigElement("side")
|
||||
this.side = config.<String>getConfigElement("side")
|
||||
.map(DependencySide::valueOf)
|
||||
.orElse(DependencySide.BOTH);
|
||||
var referralUrl = config.<String>getConfigElement("referralUrl")
|
||||
this.referralUrl = config.<String>getConfigElement("referralUrl")
|
||||
.map(StringUtils::toURL);
|
||||
}
|
||||
|
||||
return new ModVersion(
|
||||
new Holder<>(owner),
|
||||
modId, versionRange, mandatory, ordering, side, referralUrl
|
||||
);
|
||||
|
||||
@Override
|
||||
public String getModId()
|
||||
{
|
||||
return modId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IModInfo getOwner() {
|
||||
return owner.value;
|
||||
public VersionRange getVersionRange()
|
||||
{
|
||||
return versionRange;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOwner(IModInfo newOwner) {
|
||||
owner.value = newOwner;
|
||||
public boolean isMandatory()
|
||||
{
|
||||
return mandatory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Ordering getOrdering()
|
||||
{
|
||||
return ordering;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DependencySide getSide()
|
||||
{
|
||||
return side;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOwner(final IModInfo owner)
|
||||
{
|
||||
this.owner = owner;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IModInfo getOwner()
|
||||
{
|
||||
return owner;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<URL> getReferralURL() {
|
||||
return referralUrl;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+16
-31
@@ -9,64 +9,49 @@ import cpw.mods.jarhandling.JarMetadata;
|
||||
import net.minecraftforge.forgespi.locating.IModFile;
|
||||
import java.lang.module.ModuleDescriptor;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
@ApiStatus.Internal
|
||||
public final class ModJarMetadata implements JarMetadata {
|
||||
private static final String AUTOMATIC_MODULE_NAME = "Automatic-Module-Name";
|
||||
private IModFile modFile;
|
||||
private String name;
|
||||
private String version;
|
||||
private ModuleDescriptor descriptor;
|
||||
|
||||
ModJarMetadata() { }
|
||||
ModJarMetadata() {
|
||||
}
|
||||
|
||||
public void setModFile(IModFile file) {
|
||||
this.modFile = file;
|
||||
var mods = this.modFile.getModFileInfo().getMods();
|
||||
|
||||
if (!mods.isEmpty()) {
|
||||
var main = mods.get(0);
|
||||
this.name = main.getModId();
|
||||
this.version = main.getVersion().toString();
|
||||
}
|
||||
|
||||
var jar = file.getSecureJar();
|
||||
var aname = jar.moduleDataProvider().getManifest().getMainAttributes().getValue(AUTOMATIC_MODULE_NAME);
|
||||
if (aname != null)
|
||||
this.name = aname;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return this.name;
|
||||
return modFile.getModFileInfo().moduleName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String version() {
|
||||
return this.version;
|
||||
return modFile.getModFileInfo().versionString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModuleDescriptor descriptor() {
|
||||
if (descriptor != null)
|
||||
return descriptor;
|
||||
|
||||
if (descriptor != null) return descriptor;
|
||||
var bld = ModuleDescriptor.newAutomaticModule(name())
|
||||
.version(version())
|
||||
.packages(modFile.getSecureJar().getPackages());
|
||||
|
||||
for (var provider : modFile.getSecureJar().getProviders()) {
|
||||
if (provider.providers().isEmpty())
|
||||
continue;
|
||||
|
||||
bld.provides(provider.serviceName(), provider.providers());
|
||||
}
|
||||
|
||||
.version(version())
|
||||
.packages(modFile.getSecureJar().getPackages());
|
||||
modFile.getSecureJar().getProviders().stream()
|
||||
.filter(p -> !p.providers().isEmpty())
|
||||
.forEach(p -> bld.provides(p.serviceName(), p.providers()));
|
||||
modFile.getModFileInfo().usesServices().forEach(bld::uses);
|
||||
descriptor = bld.build();
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
public IModFile modFile() {
|
||||
return modFile;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == this) return true;
|
||||
|
||||
+3
-3
@@ -64,7 +64,7 @@ public class ModValidator {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static List<ModFile> validateFiles(final List<ModFile> mods) {
|
||||
private List<ModFile> validateFiles(final List<ModFile> mods) {
|
||||
final List<ModFile> brokenFiles = new ArrayList<>();
|
||||
for (Iterator<ModFile> iterator = mods.iterator(); iterator.hasNext();) {
|
||||
ModFile modFile = iterator.next();
|
||||
@@ -106,8 +106,8 @@ public class ModValidator {
|
||||
|
||||
private List<EarlyLoadingException.ExceptionData> validateLanguages() {
|
||||
List<EarlyLoadingException.ExceptionData> errorData = new ArrayList<>();
|
||||
for (Iterator<ModFile> iterator = this.candidateMods.iterator(); iterator.hasNext();) {
|
||||
var modFile = iterator.next();
|
||||
for (Iterator<ModFile> iterator = this.candidateMods.iterator(); iterator.hasNext(); ) {
|
||||
final ModFile modFile = iterator.next();
|
||||
try {
|
||||
modFile.identifyLanguage();
|
||||
} catch (EarlyLoadingException e) {
|
||||
|
||||
+15
-34
@@ -6,8 +6,6 @@
|
||||
package net.minecraftforge.fml.loading.moddiscovery;
|
||||
|
||||
import com.electronwill.nightconfig.core.UnmodifiableConfig;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
import net.minecraftforge.forgespi.language.IConfigurable;
|
||||
import net.minecraftforge.forgespi.language.IModFileInfo;
|
||||
|
||||
@@ -18,12 +16,11 @@ import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
|
||||
@ApiStatus.Internal
|
||||
final class NightConfigWrapper implements IConfigurable {
|
||||
class NightConfigWrapper implements IConfigurable {
|
||||
private final UnmodifiableConfig config;
|
||||
private IModFileInfo file;
|
||||
|
||||
@@ -31,51 +28,35 @@ final class NightConfigWrapper implements IConfigurable {
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
private NightConfigWrapper(UnmodifiableConfig config, IModFileInfo file) {
|
||||
this.config = config;
|
||||
this.file = file;
|
||||
}
|
||||
|
||||
void setFile(IModFileInfo file) {
|
||||
NightConfigWrapper setFile(IModFileInfo file) {
|
||||
this.file = file;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Optional<T> getConfigElement(String key) {
|
||||
var path = List.of(key);
|
||||
return Optional.ofNullable(validate(this.config.get(path), path));
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> Optional<T> getConfigElement(final String... key) {
|
||||
var path = asList(key);
|
||||
return Optional.ofNullable(validate(this.config.get(path), path));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T> T validate(@Nullable T value, List<String> path) {
|
||||
if (value instanceof UnmodifiableConfig cfg) {
|
||||
// New Night config doesn't implement valueMap(), so do a copy.
|
||||
var entries = cfg.entrySet();
|
||||
var builder = ImmutableMap.builderWithExpectedSize(entries.size());
|
||||
for (var e : entries)
|
||||
builder.put(e.getKey(), e.getValue());
|
||||
return (T) builder.build();
|
||||
} else if (value instanceof ArrayList<?> al && !al.isEmpty() && al.getFirst() instanceof UnmodifiableConfig) {
|
||||
throw new InvalidModFileException("The configuration path " + path + " is invalid. I wasn't expecting a multi-object list - remove one of the [[ ]]", file);
|
||||
}
|
||||
return value;
|
||||
return this.config.getOptional(path).map(value -> {
|
||||
if (value instanceof UnmodifiableConfig) {
|
||||
return (T) ((UnmodifiableConfig) value).valueMap();
|
||||
} else if (value instanceof ArrayList<?> al && al.size() > 0 && al.get(0) instanceof UnmodifiableConfig) {
|
||||
throw new InvalidModFileException("The configuration path " + path + " is invalid. I wasn't expecting a multi-object list - remove one of the [[ ]]", file);
|
||||
}
|
||||
return (T) value;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<? extends IConfigurable> getConfigList(final String... key) {
|
||||
final List<String> path = asList(key);
|
||||
if (this.config.contains(path) && !(this.config.get(path) instanceof Collection)) {
|
||||
throw new InvalidModFileException("The configuration path " + path + " is invalid. Expecting a collection!", file);
|
||||
throw new InvalidModFileException("The configuration path "+path+" is invalid. Expecting a collection!", file);
|
||||
}
|
||||
final Collection<UnmodifiableConfig> nestedConfigs = this.config.getOrElse(path, ArrayList::new);
|
||||
return nestedConfigs.stream()
|
||||
.map(conf -> new NightConfigWrapper(conf, file))
|
||||
.map(NightConfigWrapper::new)
|
||||
.map(cw->cw.setFile(file))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
@@ -21,36 +21,37 @@ import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
|
||||
@ApiStatus.Internal
|
||||
record Scanner(ModFile fileToScan, ModFileScanData result) {
|
||||
class Scanner {
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
private static final boolean DEBUG = LOGGER.isDebugEnabled(LogMarkers.SCAN);
|
||||
private final ModFile fileToScan;
|
||||
|
||||
public Scanner(ModFile fileToScan) {
|
||||
this(fileToScan, new ModFileScanData());
|
||||
public Scanner(final ModFile fileToScan) {
|
||||
this.fileToScan = fileToScan;
|
||||
}
|
||||
|
||||
public ModFileScanData scan() {
|
||||
ModFileScanData result = new ModFileScanData();
|
||||
result.addModFileInfo(fileToScan.getModFileInfo());
|
||||
fileToScan.scanFile(this::fileVisitor);
|
||||
fileToScan.scanFile(p -> fileVisitor(p, result));
|
||||
final List<IModLanguageProvider> loaders = fileToScan.getLoaders();
|
||||
if (loaders != null) {
|
||||
for (IModLanguageProvider loader : loaders) {
|
||||
if (DEBUG) LOGGER.debug("Scanning {} with language loader {}", fileToScan.getFilePath(), loader.name());
|
||||
loaders.forEach(loader -> {
|
||||
LOGGER.debug(LogMarkers.SCAN, "Scanning {} with language loader {}", fileToScan.getFilePath(), loader.name());
|
||||
loader.getFileVisitor().accept(result);
|
||||
}
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private void fileVisitor(final Path path) {
|
||||
try (InputStream in = Files.newInputStream(path)) {
|
||||
private void fileVisitor(final Path path, final ModFileScanData result) {
|
||||
LOGGER.debug(LogMarkers.SCAN,"Scanning {} path {}", fileToScan, path);
|
||||
try (InputStream in = Files.newInputStream(path)){
|
||||
ModClassVisitor mcv = new ModClassVisitor();
|
||||
ClassReader cr = new ClassReader(in);
|
||||
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
|
||||
if (DEBUG) LOGGER.warn("Failed scanning {} path {}", fileToScan, path);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,8 +24,8 @@ import java.util.function.Supplier;
|
||||
@ApiStatus.Internal
|
||||
class ArgumentList {
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
private final List<Supplier<String[]>> entries = new ArrayList<>();
|
||||
private final Map<String, EntryValue> values = new HashMap<>();
|
||||
private List<Supplier<String[]>> entries = new ArrayList<>();
|
||||
private Map<String, EntryValue> values = new HashMap<>();
|
||||
|
||||
public static ArgumentList from(String... args) {
|
||||
ArgumentList ret = new ArgumentList();
|
||||
@@ -77,8 +77,8 @@ class ArgumentList {
|
||||
|
||||
public String[] getArguments() {
|
||||
return entries.stream()
|
||||
.flatMap(e -> Arrays.stream(e.get()))
|
||||
.toArray(String[]::new);
|
||||
.flatMap(e -> Arrays.asList(e.get()).stream())
|
||||
.toArray(size -> new String[size]);
|
||||
}
|
||||
|
||||
public boolean hasValue(String key) {
|
||||
@@ -122,7 +122,7 @@ class ArgumentList {
|
||||
return ent.getValue();
|
||||
}
|
||||
|
||||
private static final class EntryValue implements Supplier<String[]> {
|
||||
private class EntryValue implements Supplier<String[]> {
|
||||
private final String prefix;
|
||||
private final String key;
|
||||
private final boolean split;
|
||||
|
||||
+1
-1
@@ -40,7 +40,7 @@ abstract class CommonDevLaunchHandler extends CommonLaunchHandler {
|
||||
String username = args.get("username");
|
||||
if (username != null) { // Replace '#' placeholders with random numbers
|
||||
Matcher m = Pattern.compile("#+").matcher(username);
|
||||
StringBuilder replaced = new StringBuilder();
|
||||
StringBuffer replaced = new StringBuffer();
|
||||
while (m.find()) {
|
||||
m.appendReplacement(replaced, getRandomNumbers(m.group().length()));
|
||||
}
|
||||
|
||||
+8
-2
@@ -7,6 +7,7 @@ package net.minecraftforge.fml.loading.targets;
|
||||
|
||||
import com.mojang.logging.LogUtils;
|
||||
import cpw.mods.modlauncher.api.ILaunchHandlerService;
|
||||
import cpw.mods.modlauncher.api.ITransformingClassLoaderBuilder;
|
||||
import cpw.mods.modlauncher.api.ServiceRunner;
|
||||
import net.minecraftforge.fml.loading.FMLLoader;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
@@ -18,6 +19,7 @@ import org.slf4j.Logger;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -45,6 +47,10 @@ public abstract class CommonLaunchHandler implements ILaunchHandlerService {
|
||||
|
||||
public abstract List<Path> getMinecraftPaths();
|
||||
|
||||
@Override
|
||||
public void configureTransformationClassLoader(final ITransformingClassLoaderBuilder builder) {
|
||||
}
|
||||
|
||||
protected String[] preLaunch(String[] arguments, ModuleLayer layer) {
|
||||
URI uri;
|
||||
try (var reader = layer.configuration().findModule("net.minecraftforge.fmlloader").orElseThrow().reference().open()) {
|
||||
@@ -73,7 +79,7 @@ public abstract class CommonLaunchHandler implements ILaunchHandlerService {
|
||||
protected static final LaunchType CLIENT = new LaunchType("client", "minecraft", "net.minecraft.client.main.Main", Dist.CLIENT, false);
|
||||
protected static final LaunchType DATA = new LaunchType("data", "minecraft", "net.minecraft.data.Main", Dist.CLIENT, true);
|
||||
protected static final LaunchType SERVER = new LaunchType("server", "minecraft", "net.minecraft.server.Main", Dist.DEDICATED_SERVER, false);
|
||||
protected static final LaunchType SERVER_GAMETEST = new LaunchType("server_gametest", "net.minecraftforge.forge", "net.minecraftforge.gametest.GameTestMain", Dist.DEDICATED_SERVER, false);
|
||||
protected static final LaunchType SERVER_GAMETEST = new LaunchType("server_gametest", "forge", "net.minecraftforge.gametest.GameTestMain", Dist.DEDICATED_SERVER, false);
|
||||
|
||||
protected void runTarget(String module, String target, final String[] arguments, final ModuleLayer layer) throws Throwable {
|
||||
var mod = layer.findModule(module).orElse(null);
|
||||
@@ -101,7 +107,7 @@ public abstract class CommonLaunchHandler implements ILaunchHandlerService {
|
||||
len += 2;
|
||||
}
|
||||
str = str.substring(0, str.length() - len);
|
||||
var path = Path.of(URI.create(str));
|
||||
var path = Paths.get(URI.create(str));
|
||||
return path;
|
||||
}
|
||||
}
|
||||
+5
-5
@@ -9,7 +9,7 @@ import java.util.List;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
@ApiStatus.Internal
|
||||
sealed abstract class ForgeDevLaunchHandler extends CommonDevLaunchHandler {
|
||||
abstract class ForgeDevLaunchHandler extends CommonDevLaunchHandler {
|
||||
private ForgeDevLaunchHandler(LaunchType type) {
|
||||
super(type, "forge_dev_");
|
||||
}
|
||||
@@ -33,25 +33,25 @@ sealed abstract class ForgeDevLaunchHandler extends CommonDevLaunchHandler {
|
||||
return List.of(filtered);
|
||||
}
|
||||
|
||||
public static final class Client extends ForgeDevLaunchHandler {
|
||||
public static class Client extends ForgeDevLaunchHandler {
|
||||
public Client() {
|
||||
super(CLIENT);
|
||||
}
|
||||
}
|
||||
|
||||
public static final class Data extends ForgeDevLaunchHandler {
|
||||
public static class Data extends ForgeDevLaunchHandler {
|
||||
public Data() {
|
||||
super(DATA);
|
||||
}
|
||||
}
|
||||
|
||||
public static final class Server extends ForgeDevLaunchHandler {
|
||||
public static class Server extends ForgeDevLaunchHandler {
|
||||
public Server() {
|
||||
super(SERVER);
|
||||
}
|
||||
}
|
||||
|
||||
public static final class ServerGameTest extends ForgeDevLaunchHandler {
|
||||
public static class ServerGameTest extends ForgeDevLaunchHandler {
|
||||
public ServerGameTest() {
|
||||
super(SERVER_GAMETEST);
|
||||
}
|
||||
|
||||
+19
-35
@@ -35,7 +35,7 @@ import net.minecraftforge.fml.loading.moddiscovery.AbstractModProvider;
|
||||
import net.minecraftforge.forgespi.locating.IModLocator;
|
||||
|
||||
@ApiStatus.Internal
|
||||
public final class ForgeDevLocator extends AbstractModProvider implements IModLocator {
|
||||
public class ForgeDevLocator extends AbstractModProvider implements IModLocator {
|
||||
private static final String PACK_META = "pack.mcmeta";
|
||||
|
||||
@Override
|
||||
@@ -60,7 +60,7 @@ public final class ForgeDevLocator extends AbstractModProvider implements IModLo
|
||||
return ret;
|
||||
}
|
||||
|
||||
private static List<Path> getMods() {
|
||||
private List<Path> getMods() {
|
||||
// Forge is an exploded directory as well
|
||||
var minecraft = ForgeDevLaunchHandler.getPathFromResource("net/minecraft/client/Minecraft.class");
|
||||
var forge = ForgeDevLaunchHandler.getPathFromResource("net/minecraftforge/common/MinecraftForge.class");
|
||||
@@ -84,31 +84,30 @@ public final class ForgeDevLocator extends AbstractModProvider implements IModLo
|
||||
return ret;
|
||||
}
|
||||
|
||||
private static List<Path> explodeTestMods(Path path) {
|
||||
private List<Path> explodeTestMods(Path path) {
|
||||
var mod = new ArrayList<Path>();
|
||||
var memory = Jimfs.newFileSystem();
|
||||
|
||||
// First lets find all class files, that have the @Mod annotation and map packages to modids.
|
||||
var packages = findTestModPackages(path);
|
||||
for (var entry : packages.entrySet()) {
|
||||
var pkg = entry.getKey() + '/';
|
||||
var pkg = entry.getKey();
|
||||
var modids = entry.getValue();
|
||||
|
||||
// Find resource directories for every mod in this group
|
||||
var paths = new LinkedHashSet<Path>();
|
||||
var resourcePaths = new LinkedHashSet<Path>();
|
||||
for (var modid : modids) {
|
||||
var rsc = path.resolve(modid);
|
||||
if (Files.exists(rsc))
|
||||
paths.add(rsc);
|
||||
resourcePaths.add(rsc);
|
||||
}
|
||||
|
||||
var root = memory.getPath(modids.iterator().next()); // use the first modid as our root
|
||||
buildModsToml(paths, modids, root);
|
||||
buildPackMeta(paths, root);
|
||||
moveModuleInfo(paths, root);
|
||||
buildModsToml(resourcePaths, modids, root);
|
||||
buildPackMeta(resourcePaths, root);
|
||||
|
||||
if (Files.exists(root))
|
||||
paths.add(root);
|
||||
resourcePaths.add(root);
|
||||
|
||||
// We want just the class files from the root of the input paths. So make a new union with a filter.
|
||||
var classes = UnionHelper.newFileSystem(
|
||||
@@ -122,10 +121,14 @@ public final class ForgeDevLocator extends AbstractModProvider implements IModLo
|
||||
}, new Path[] { path }
|
||||
);
|
||||
|
||||
paths.addFirst(classes.getRootDirectories().iterator().next());
|
||||
// And we want the resources, and none of the classes, just in case things get kinda fucky and somehow a package overlaps a sub package {shading?}
|
||||
var resources = UnionHelper.newFileSystem((name, base) -> !name.endsWith(".class"), resourcePaths.toArray(Path[]::new));
|
||||
|
||||
// Union of unions, Yay!
|
||||
var union = UnionHelper.newFileSystem(null, paths.stream().toArray(Path[]::new));
|
||||
var union = UnionHelper.newFileSystem(null, new Path[] {
|
||||
classes.getRootDirectories().iterator().next(),
|
||||
resources.getRootDirectories().iterator().next()
|
||||
});
|
||||
mod.add(union.getRootDirectories().iterator().next());
|
||||
}
|
||||
|
||||
@@ -134,7 +137,7 @@ public final class ForgeDevLocator extends AbstractModProvider implements IModLo
|
||||
|
||||
// Find all the @Mods so we can generate tomls, and so we can pick packages. Right now it doesn't allow mods in parent directories,
|
||||
// I could make it merge all the way up, but I think this would be fine.
|
||||
private static Map<String, Set<String>> findTestModPackages(Path path) {
|
||||
private Map<String, Set<String>> findTestModPackages(Path path) {
|
||||
var mods = new HashMap<String, Set<String>>();
|
||||
try (var files = Files.walk(path)) {
|
||||
var classes = files
|
||||
@@ -187,7 +190,7 @@ public final class ForgeDevLocator extends AbstractModProvider implements IModLo
|
||||
}
|
||||
|
||||
// Builds or update the mods.toml file for all @Mods in this package
|
||||
private static void buildModsToml(Set<Path> resources, Set<String> modids, Path root) {
|
||||
private void buildModsToml(Set<Path> resources, Set<String> modids, Path root) {
|
||||
var toml = resources.stream()
|
||||
.map(p -> p.resolve(MODS_TOML))
|
||||
.filter(Files::exists)
|
||||
@@ -223,7 +226,7 @@ public final class ForgeDevLocator extends AbstractModProvider implements IModLo
|
||||
}
|
||||
|
||||
for (var modid : modids) {
|
||||
if (modlist.stream().noneMatch(c -> modid.equals(c.get("modId")))) {
|
||||
if (!modlist.stream().anyMatch(c -> modid.equals(c.get("modId")))) {
|
||||
modified = true;
|
||||
var tmp = Config.inMemory();
|
||||
tmp.set("modId", modid);
|
||||
@@ -245,7 +248,7 @@ public final class ForgeDevLocator extends AbstractModProvider implements IModLo
|
||||
}
|
||||
|
||||
// This is optional, it just hides a warning screen when starting up. I should probably remove this once I restructure how data gen for test mods work and make it generate there.
|
||||
private static void buildPackMeta(Set<Path> paths, Path root) {
|
||||
private void buildPackMeta(Set<Path> paths, Path root) {
|
||||
var existing = paths.stream()
|
||||
.map(p -> p.resolve(PACK_META))
|
||||
.filter(Files::exists)
|
||||
@@ -271,25 +274,6 @@ public final class ForgeDevLocator extends AbstractModProvider implements IModLo
|
||||
}
|
||||
}
|
||||
|
||||
private static void moveModuleInfo(Set<Path> paths, Path root) {
|
||||
var existing = paths.stream()
|
||||
.map(p -> p.resolve("module-info.dat"))
|
||||
.filter(Files::exists)
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
|
||||
if (existing == null)
|
||||
return;
|
||||
|
||||
var target = root.resolve("module-info.class");
|
||||
try {
|
||||
Files.createDirectories(target.getParent());
|
||||
Files.copy(existing, target);
|
||||
} catch (IOException e) {
|
||||
throw new IllegalStateException("Failed to copy module-info.dat to memory: " + target, e);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <E extends Throwable, R> R sneak(Throwable e) throws E {
|
||||
throw (E)e;
|
||||
|
||||
+3
-3
@@ -10,7 +10,7 @@ import java.util.List;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
@ApiStatus.Internal
|
||||
sealed abstract class ForgeProdLaunchHandler extends CommonLaunchHandler {
|
||||
abstract class ForgeProdLaunchHandler extends CommonLaunchHandler {
|
||||
protected ForgeProdLaunchHandler(LaunchType type) {
|
||||
super(type, "forge_");
|
||||
}
|
||||
@@ -18,7 +18,7 @@ sealed abstract class ForgeProdLaunchHandler extends CommonLaunchHandler {
|
||||
@Override public String getNaming() { return "mcp"; }
|
||||
@Override public boolean isProduction() { return true; }
|
||||
|
||||
public static final class Client extends ForgeProdLaunchHandler {
|
||||
public static class Client extends ForgeProdLaunchHandler {
|
||||
public Client() {
|
||||
super(CLIENT);
|
||||
}
|
||||
@@ -29,7 +29,7 @@ sealed abstract class ForgeProdLaunchHandler extends CommonLaunchHandler {
|
||||
}
|
||||
}
|
||||
|
||||
public static final class Server extends ForgeProdLaunchHandler {
|
||||
public static class Server extends ForgeProdLaunchHandler {
|
||||
public Server() {
|
||||
super(SERVER);
|
||||
}
|
||||
|
||||
+5
-5
@@ -14,7 +14,7 @@ import org.jetbrains.annotations.ApiStatus;
|
||||
import cpw.mods.jarhandling.SecureJar;
|
||||
|
||||
@ApiStatus.Internal
|
||||
sealed abstract class ForgeUserdevLaunchHandler extends CommonDevLaunchHandler {
|
||||
abstract class ForgeUserdevLaunchHandler extends CommonDevLaunchHandler {
|
||||
private ForgeUserdevLaunchHandler(LaunchType type) {
|
||||
super(type, "forge_userdev_");
|
||||
}
|
||||
@@ -33,25 +33,25 @@ sealed abstract class ForgeUserdevLaunchHandler extends CommonDevLaunchHandler {
|
||||
return List.of(minecraft);
|
||||
}
|
||||
|
||||
public static final class Client extends ForgeUserdevLaunchHandler {
|
||||
public static class Client extends ForgeUserdevLaunchHandler {
|
||||
public Client() {
|
||||
super(CLIENT);
|
||||
}
|
||||
}
|
||||
|
||||
public static final class Data extends ForgeUserdevLaunchHandler {
|
||||
public static class Data extends ForgeUserdevLaunchHandler {
|
||||
public Data() {
|
||||
super(DATA);
|
||||
}
|
||||
}
|
||||
|
||||
public static final class Server extends ForgeUserdevLaunchHandler {
|
||||
public static class Server extends ForgeUserdevLaunchHandler {
|
||||
public Server() {
|
||||
super(SERVER);
|
||||
}
|
||||
}
|
||||
|
||||
public static final class ServerGameTest extends ForgeUserdevLaunchHandler {
|
||||
public static class ServerGameTest extends ForgeUserdevLaunchHandler {
|
||||
public ServerGameTest() {
|
||||
super(SERVER_GAMETEST);
|
||||
}
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ import net.minecraftforge.fml.loading.moddiscovery.AbstractModProvider;
|
||||
import net.minecraftforge.forgespi.locating.IModLocator;
|
||||
|
||||
@ApiStatus.Internal
|
||||
public final class ForgeUserdevLocator extends AbstractModProvider implements IModLocator {
|
||||
public class ForgeUserdevLocator extends AbstractModProvider implements IModLocator {
|
||||
@Override
|
||||
public String name() {
|
||||
return "forge_userdev_locator";
|
||||
|
||||
+2
-1
@@ -25,7 +25,7 @@ import java.util.Set;
|
||||
*/
|
||||
public class StronglyConnectedComponentDetector<T> {
|
||||
private final Graph<T> graph;
|
||||
private final Map<T, Integer> ids = new HashMap<>();
|
||||
private Map<T, Integer> ids;
|
||||
private T[] elements;
|
||||
private int[] dfn;
|
||||
private int[] low;
|
||||
@@ -49,6 +49,7 @@ public class StronglyConnectedComponentDetector<T> {
|
||||
private void calculate() {
|
||||
components = new HashSet<>();
|
||||
int t = 0;
|
||||
ids = new HashMap<>();
|
||||
Set<T> nodes = graph.nodes();
|
||||
elements = (T[]) new Object[nodes.size()];
|
||||
for (T node : nodes) {
|
||||
|
||||
+4
-4
@@ -5,11 +5,11 @@ org.gradle.daemon=false
|
||||
org.gradle.parallel=true
|
||||
|
||||
JAVA_VERSION=21
|
||||
MC_VERSION=1.21.1
|
||||
MC_NEXT_VERSION=1.22
|
||||
MCP_VERSION=20240808.132146
|
||||
MC_VERSION=1.20.6
|
||||
MC_NEXT_VERSION=1.21
|
||||
MCP_VERSION=20240429.135109
|
||||
MAPPING_CHANNEL=official
|
||||
MAPPING_VERSION=1.21.1
|
||||
MAPPING_VERSION=1.20.6
|
||||
|
||||
// Set to true before the first build of a new MC version, so we don't do compatibility checks
|
||||
CHECK_COMPATIBILITY=false
|
||||
|
||||
@@ -13,8 +13,6 @@ dependencies {
|
||||
compileOnly(libs.nulls)
|
||||
implementation(project(':fmlloader'))
|
||||
implementation(project(':fmlcore'))
|
||||
implementation(libs.unsafe)
|
||||
implementation(libs.securemodules)
|
||||
}
|
||||
|
||||
java {
|
||||
@@ -59,7 +57,7 @@ publishing {
|
||||
license PomUtils.Licenses.LGPLv2_1
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
repositories {
|
||||
maven gradleutils.publishingForgeMaven
|
||||
}
|
||||
|
||||
+15
-2
@@ -31,8 +31,21 @@ public class FMLJavaModLanguageProvider implements IModLanguageProvider
|
||||
{
|
||||
private static final Logger LOGGER = LogManager.getLogger();
|
||||
|
||||
private record FMLModTarget(String className, String modId) implements IModLanguageProvider.IModLanguageLoader {
|
||||
private static class FMLModTarget implements IModLanguageProvider.IModLanguageLoader {
|
||||
private static final Logger LOGGER = FMLJavaModLanguageProvider.LOGGER;
|
||||
private final String className;
|
||||
private final String modId;
|
||||
|
||||
private FMLModTarget(String className, String modId)
|
||||
{
|
||||
this.className = className;
|
||||
this.modId = modId;
|
||||
}
|
||||
|
||||
public String getModId()
|
||||
{
|
||||
return modId;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
@@ -81,7 +94,7 @@ public class FMLJavaModLanguageProvider implements IModLanguageProvider
|
||||
.filter(ad -> ad.annotationType().equals(MODANNOTATION))
|
||||
.peek(ad -> LOGGER.debug(SCAN, "Found @Mod class {} with id {}", ad.clazz().getClassName(), ad.annotationData().get("value")))
|
||||
.map(ad -> new FMLModTarget(ad.clazz().getClassName(), (String)ad.annotationData().get("value")))
|
||||
.collect(Collectors.toMap(FMLModTarget::modId, Function.identity(), (a,b)->a));
|
||||
.collect(Collectors.toMap(FMLModTarget::getModId, Function.identity(), (a,b)->a));
|
||||
scanResult.addLanguageLoader(modTargetMap);
|
||||
};
|
||||
}
|
||||
|
||||
+1
-7
@@ -8,9 +8,6 @@ package net.minecraftforge.fml.javafmlmod;
|
||||
import net.minecraftforge.eventbus.api.IEventBus;
|
||||
import net.minecraftforge.fml.ModLoadingContext;
|
||||
|
||||
/**
|
||||
* Use the context provided by your language loader in your mod's constructor
|
||||
*/
|
||||
public class FMLJavaModLoadingContext extends ModLoadingContext
|
||||
{
|
||||
private final FMLModContainer container;
|
||||
@@ -27,9 +24,6 @@ public class FMLJavaModLoadingContext extends ModLoadingContext
|
||||
return container.getEventBus();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@link FMLModContainer} by default.
|
||||
*/
|
||||
@Override
|
||||
public FMLModContainer getContainer() {
|
||||
return container;
|
||||
@@ -41,7 +35,7 @@ public class FMLJavaModLoadingContext extends ModLoadingContext
|
||||
*
|
||||
* @deprecated use {@link FMLJavaModLoadingContext} in your mod constructor
|
||||
*/
|
||||
@Deprecated(forRemoval = true, since="1.21.1")
|
||||
@Deprecated(forRemoval = true, since = "1.21.1")
|
||||
public static FMLJavaModLoadingContext get() {
|
||||
return ModLoadingContext.get().extension();
|
||||
}
|
||||
|
||||
+4
-77
@@ -16,21 +16,15 @@ import net.minecraftforge.fml.ModLoadingStage;
|
||||
import net.minecraftforge.fml.event.IModBusEvent;
|
||||
import net.minecraftforge.forgespi.language.IModInfo;
|
||||
import net.minecraftforge.forgespi.language.ModFileScanData;
|
||||
import net.minecraftforge.unsafe.UnsafeHacks;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.logging.log4j.Marker;
|
||||
import org.apache.logging.log4j.MarkerManager;
|
||||
|
||||
import cpw.mods.jarhandling.SecureJar;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.jar.Attributes;
|
||||
|
||||
public class FMLModContainer extends ModContainer {
|
||||
private static final Logger LOGGER = LogManager.getLogger();
|
||||
@@ -46,90 +40,23 @@ public class FMLModContainer extends ModContainer {
|
||||
LOGGER.debug(LOADING,"Creating FMLModContainer instance for {}", className);
|
||||
this.scanResults = modFileScanResults;
|
||||
activityMap.put(ModLoadingStage.CONSTRUCT, this::constructMod);
|
||||
this.eventBus = BusBuilder.builder().setExceptionHandler(FMLModContainer::onEventFailed).setTrackPhases(false).markerType(IModBusEvent.class).useModLauncher().build();
|
||||
this.eventBus = BusBuilder.builder().setExceptionHandler(this::onEventFailed).setTrackPhases(false).markerType(IModBusEvent.class).useModLauncher().build();
|
||||
this.configHandler = Optional.of(ce->this.eventBus.post(ce.self()));
|
||||
this.contextExtension = () -> context;
|
||||
|
||||
try {
|
||||
var moduleName = info.getOwningFile().moduleName();
|
||||
var module = gameLayer.findModule(moduleName)
|
||||
.orElseThrow(() -> new IllegalStateException("Failed to find " + moduleName + " in " + gameLayer));
|
||||
|
||||
openModules(gameLayer, module, info.getOwningFile().getFile().getSecureJar());
|
||||
|
||||
modClass = Class.forName(module, className);
|
||||
LOGGER.debug(LOADING,"Loaded modclass {}/{} with {}", modClass.getModule().getName(), modClass.getName(), modClass.getClassLoader());
|
||||
LOGGER.trace(LOADING,"Loaded modclass {} with {}", modClass.getName(), modClass.getClassLoader());
|
||||
} catch (Throwable e) {
|
||||
LOGGER.error(LOADING, "Failed to load class {}", className, e);
|
||||
throw new ModLoadingException(info, ModLoadingStage.CONSTRUCT, "fml.modloading.failedtoloadmodclass", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* Reads the Add-Exports and Add-Opens attributes (see note) from a mod file and
|
||||
* attempts to apply them. This differers from the JEP in two significant ways:
|
||||
* 1) Instead of opening things to ALL-UNNAMED it instead only opens to the
|
||||
* mod's module itself. Because I don't want other mods accidently relying
|
||||
* on transitive behavior
|
||||
* 2) It is read from all mods not just the executable jars.
|
||||
*
|
||||
* From <a href = "https://openjdk.org/jeps/261">JEP 261: Module System</a>
|
||||
*
|
||||
* Two new JDK-specific JAR-file manifest attributes are defined to correspond
|
||||
* to the --add-exports and --add-opens command-line options:
|
||||
* Add-Exports: <module>/<package>( <module>/<package>)*
|
||||
* Add-Opens: <module>/<package>( <module>/<package>)*
|
||||
*
|
||||
* The value of each attribute is a space-separated list of slash-separated
|
||||
* module-name/package-name pairs. A >module</>package< pair in
|
||||
* the value of an Add-Exports attribute has the same meaning as the
|
||||
* command-line option --add-exports >module</>package<=ALL-UNNAMED.
|
||||
*
|
||||
* A >module</>package< pair in the value of an Add-Opens attribute has the
|
||||
* same meaning as the command-line option --add-opens >module</>package<=ALL-UNNAMED.
|
||||
*
|
||||
* Each attribute can occur at most once, in the main section of a MANIFEST.MF file.
|
||||
* A particular pair can be listed more than once. If a specified module was not
|
||||
* resolved, or if a specified package does not exist, then the corresponding pair
|
||||
* is ignored.
|
||||
*/
|
||||
private static void openModules(ModuleLayer layer, Module self, SecureJar jar) throws NoSuchMethodException, SecurityException, IllegalAccessException, InvocationTargetException {
|
||||
var manifest = jar.moduleDataProvider().getManifest().getMainAttributes();
|
||||
addOpenOrExports(layer, self, true, manifest);
|
||||
addOpenOrExports(layer, self, false, manifest);
|
||||
}
|
||||
|
||||
private static void addOpenOrExports(ModuleLayer layer, Module self, boolean open, Attributes attrs) throws NoSuchMethodException, SecurityException, IllegalAccessException, InvocationTargetException {
|
||||
var key = open ? "Add-Opens" : "Add-Exports";
|
||||
var entry = attrs.getValue(key);
|
||||
if (entry == null)
|
||||
return;
|
||||
|
||||
for (var pair : entry.split(" ")) {
|
||||
var pts = pair.trim().split("/");
|
||||
if (pts.length == 2) {
|
||||
var target = layer.findModule(pts[0]).orElse(null);
|
||||
if (target == null || !target.getDescriptor().packages().contains(pts[1]))
|
||||
continue;
|
||||
addOpenOrExport(target, pts[1], self, open);
|
||||
} else {
|
||||
LOGGER.warn(LOADING, "Invalid {} entry in {}: {}", key, self.getName(), pair);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Method implAddExportsOrOpens;
|
||||
private static void addOpenOrExport(Module target, String pkg, Module reader, boolean open) throws NoSuchMethodException, SecurityException, IllegalAccessException, InvocationTargetException {
|
||||
if (implAddExportsOrOpens == null) {
|
||||
implAddExportsOrOpens = Module.class.getDeclaredMethod("implAddExportsOrOpens", String.class, Module.class, boolean.class, boolean.class);
|
||||
UnsafeHacks.setAccessible(implAddExportsOrOpens);
|
||||
}
|
||||
|
||||
LOGGER.info(LOADING, "{} {}/{} to {}", open ? "Opening" : "Exporting", target.getName(), pkg, reader.getName());
|
||||
implAddExportsOrOpens.invoke(target, pkg, reader, open, /*syncVM*/true);
|
||||
}
|
||||
|
||||
private static void onEventFailed(IEventBus iEventBus, Event event, IEventListener[] iEventListeners, int i, Throwable throwable) {
|
||||
private void onEventFailed(IEventBus iEventBus, Event event, IEventListener[] iEventListeners, int i, Throwable throwable) {
|
||||
LOGGER.error(new EventBusErrorMessage(event, i, iEventListeners, throwable));
|
||||
}
|
||||
|
||||
|
||||
+8
-1
@@ -21,12 +21,14 @@ import static net.minecraftforge.fml.loading.LogMarkers.LOADING;
|
||||
public class LowCodeModContainer extends ModContainer
|
||||
{
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
private final Object modInstance;
|
||||
private final ModFileScanData scanResults;
|
||||
private Object modInstance;
|
||||
|
||||
public LowCodeModContainer(IModInfo info, ModFileScanData modFileScanResults, ModuleLayer gameLayer)
|
||||
{
|
||||
super(info);
|
||||
LOGGER.debug(LOADING, "Creating LowCodeModContainer for {}", info.getModId());
|
||||
this.scanResults = modFileScanResults;
|
||||
this.modInstance = new Object();
|
||||
this.contextExtension = () -> null;
|
||||
this.extensionPoints.remove(IExtensionPoint.DisplayTest.class);
|
||||
@@ -43,4 +45,9 @@ public class LowCodeModContainer extends ModContainer
|
||||
{
|
||||
return modInstance;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected <T extends Event & IModBusEvent> void acceptEvent(final T e)
|
||||
{
|
||||
}
|
||||
}
|
||||
+4
@@ -5,6 +5,7 @@
|
||||
|
||||
package net.minecraftforge.fml.mclanguageprovider;
|
||||
|
||||
import net.minecraftforge.fml.ModContainer;
|
||||
import net.minecraftforge.forgespi.language.ILifecycleEvent;
|
||||
import net.minecraftforge.forgespi.language.IModInfo;
|
||||
import net.minecraftforge.forgespi.language.IModLanguageProvider;
|
||||
@@ -14,11 +15,14 @@ import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import static net.minecraftforge.fml.Logging.LOADING;
|
||||
|
||||
import net.minecraftforge.forgespi.language.IModLanguageProvider.IModLanguageLoader;
|
||||
|
||||
public class MinecraftModLanguageProvider implements IModLanguageProvider {
|
||||
private static final Logger LOGGER = LogManager.getLogger();
|
||||
@Override
|
||||
|
||||
@@ -46,7 +46,7 @@ public class Config
|
||||
|
||||
private static boolean validateItemName(final Object obj)
|
||||
{
|
||||
return obj instanceof final String itemName && ForgeRegistries.ITEMS.containsKey(ResourceLocation.tryParse(itemName));
|
||||
return obj instanceof final String itemName && ForgeRegistries.ITEMS.containsKey(new ResourceLocation(itemName));
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
@@ -58,7 +58,7 @@ public class Config
|
||||
|
||||
// convert the list of strings into a set of items
|
||||
items = ITEM_STRINGS.get().stream()
|
||||
.map(itemName -> ForgeRegistries.ITEMS.getValue(ResourceLocation.tryParse(itemName)))
|
||||
.map(itemName -> ForgeRegistries.ITEMS.getValue(new ResourceLocation(itemName)))
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
--- a/com/mojang/blaze3d/pipeline/RenderTarget.java
|
||||
+++ b/com/mojang/blaze3d/pipeline/RenderTarget.java
|
||||
@@ -104,7 +_,10 @@
|
||||
@@ -106,7 +_,10 @@
|
||||
GlStateManager._texParameter(3553, 34892, 0);
|
||||
GlStateManager._texParameter(3553, 10242, 33071);
|
||||
GlStateManager._texParameter(3553, 10243, 33071);
|
||||
@@ -11,7 +11,7 @@
|
||||
}
|
||||
|
||||
this.setFilterMode(9728, true);
|
||||
@@ -114,8 +_,13 @@
|
||||
@@ -116,8 +_,13 @@
|
||||
GlStateManager._texImage2D(3553, 0, 32856, this.width, this.height, 0, 6408, 5121, null);
|
||||
GlStateManager._glBindFramebuffer(36160, this.frameBufferId);
|
||||
GlStateManager._glFramebufferTexture2D(36160, 36064, 3553, this.colorTextureId, 0);
|
||||
@@ -26,7 +26,7 @@
|
||||
}
|
||||
|
||||
this.checkStatus();
|
||||
@@ -257,5 +_,26 @@
|
||||
@@ -280,5 +_,26 @@
|
||||
|
||||
public int getDepthTextureId() {
|
||||
return this.depthBufferId;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
--- a/com/mojang/blaze3d/platform/Window.java
|
||||
+++ b/com/mojang/blaze3d/platform/Window.java
|
||||
@@ -84,7 +_,8 @@
|
||||
@@ -82,7 +_,8 @@
|
||||
GLFW.glfwWindowHint(139267, 2);
|
||||
GLFW.glfwWindowHint(139272, 204801);
|
||||
GLFW.glfwWindowHint(139270, 1);
|
||||
@@ -10,7 +10,7 @@
|
||||
if (monitor != null) {
|
||||
VideoMode videomode = monitor.getPreferredVidMode(this.fullscreen ? this.preferredFullscreenVideoMode : Optional.empty());
|
||||
this.windowedX = this.x = monitor.getX() + videomode.getWidth() / 2 - this.width / 2;
|
||||
@@ -96,6 +_,7 @@
|
||||
@@ -94,6 +_,7 @@
|
||||
this.windowedX = this.x = aint1[0];
|
||||
this.windowedY = this.y = aint[0];
|
||||
}
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
GLFW.glfwMakeContextCurrent(this.window);
|
||||
GL.createCapabilities();
|
||||
@@ -254,6 +_,9 @@
|
||||
@@ -242,6 +_,9 @@
|
||||
GLFW.glfwGetFramebufferSize(this.window, aint, aint1);
|
||||
this.framebufferWidth = aint[0] > 0 ? aint[0] : 1;
|
||||
this.framebufferHeight = aint1[0] > 0 ? aint1[0] : 1;
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
--- a/com/mojang/blaze3d/vertex/BufferBuilder.java
|
||||
+++ b/com/mojang/blaze3d/vertex/BufferBuilder.java
|
||||
@@ -389,6 +_,15 @@
|
||||
return MemoryUtil.memSlice(this.buffer, p_231170_, p_231171_ - p_231170_);
|
||||
}
|
||||
|
||||
+ public void putBulkData(ByteBuffer buffer) {
|
||||
+ ensureCapacity(buffer.limit() + this.format.getVertexSize());
|
||||
+ this.buffer.position(this.nextElementByte);
|
||||
+ this.buffer.put(buffer);
|
||||
+ this.buffer.position(0);
|
||||
+ this.vertices += buffer.limit() / this.format.getVertexSize();
|
||||
+ this.nextElementByte += buffer.limit();
|
||||
+ }
|
||||
+
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
public static record DrawState(
|
||||
VertexFormat format, int vertexCount, int indexCount, VertexFormat.Mode mode, VertexFormat.IndexType indexType, boolean indexOnly, boolean sequentialIndex
|
||||
@@ -1,11 +1,11 @@
|
||||
--- a/com/mojang/blaze3d/vertex/SheetedDecalTextureGenerator.java
|
||||
+++ b/com/mojang/blaze3d/vertex/SheetedDecalTextureGenerator.java
|
||||
@@ -62,7 +_,7 @@
|
||||
public VertexConsumer setNormal(float p_344306_, float p_342091_, float p_344579_) {
|
||||
this.delegate.setNormal(p_344306_, p_342091_, p_344579_);
|
||||
Vector3f vector3f = this.normalInversePose.transform(p_344306_, p_342091_, p_344579_, this.normal);
|
||||
@@ -47,7 +_,7 @@
|
||||
@Override
|
||||
public void endVertex() {
|
||||
Vector3f vector3f = this.normalInversePose.transform(new Vector3f(this.nx, this.ny, this.nz));
|
||||
- Direction direction = Direction.getNearest(vector3f.x(), vector3f.y(), vector3f.z());
|
||||
+ Direction direction = net.minecraftforge.client.ForgeHooksClient.getNearestStable(vector3f.x(), vector3f.y(), vector3f.z());
|
||||
Vector3f vector3f1 = this.cameraInversePose.transformPosition(this.x, this.y, this.z, this.worldPos);
|
||||
vector3f1.rotateY((float) Math.PI);
|
||||
vector3f1.rotateX((float) (-Math.PI / 2));
|
||||
Vector4f vector4f = this.cameraInversePose.transform(new Vector4f(this.x, this.y, this.z, 1.0F));
|
||||
vector4f.rotateY((float) Math.PI);
|
||||
vector4f.rotateX((float) (-Math.PI / 2));
|
||||
@@ -1,24 +1,24 @@
|
||||
--- a/com/mojang/blaze3d/vertex/VertexConsumer.java
|
||||
+++ b/com/mojang/blaze3d/vertex/VertexConsumer.java
|
||||
@@ -12,7 +_,7 @@
|
||||
@@ -13,7 +_,7 @@
|
||||
import org.lwjgl.system.MemoryStack;
|
||||
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
-public interface VertexConsumer {
|
||||
+public interface VertexConsumer extends net.minecraftforge.client.extensions.IForgeVertexConsumer {
|
||||
VertexConsumer addVertex(float p_344294_, float p_342213_, float p_344859_);
|
||||
VertexConsumer vertex(double p_85945_, double p_85946_, double p_85947_);
|
||||
|
||||
VertexConsumer setColor(int p_342749_, int p_344324_, int p_343336_, int p_342831_);
|
||||
@@ -135,10 +_,11 @@
|
||||
VertexConsumer color(int p_85973_, int p_85974_, int p_85975_, int p_85976_);
|
||||
@@ -142,10 +_,11 @@
|
||||
f5 = afloat[k] * p_85992_;
|
||||
}
|
||||
|
||||
int i1 = FastColor.ARGB32.color(k, (int)f3, (int)f4, (int)f5);
|
||||
- int j1 = p_331444_[l];
|
||||
+ int j1 = applyBakedLighting(p_331444_[l], bytebuffer);
|
||||
float f10 = bytebuffer.getFloat(16);
|
||||
float f9 = bytebuffer.getFloat(20);
|
||||
Vector3f vector3f1 = matrix4f.transformPosition(f, f1, f2, new Vector3f());
|
||||
- int l = aint[k];
|
||||
+ int l = applyBakedLighting(aint[k], bytebuffer);
|
||||
float f9 = bytebuffer.getFloat(16);
|
||||
float f10 = bytebuffer.getFloat(20);
|
||||
Vector4f vector4f = matrix4f.transform(new Vector4f(f, f1, f2, 1.0F));
|
||||
+ applyBakedNormals(vector3f, bytebuffer, p_85988_.normal());
|
||||
this.addVertex(vector3f1.x(), vector3f1.y(), vector3f1.z(), i1, f10, f9, p_85993_, j1, vector3f.x(), vector3f.y(), vector3f.z());
|
||||
this.vertex(vector4f.x(), vector4f.y(), vector4f.z(), f3, f4, f5, p_335371_, f9, f10, p_85993_, l, vector3f.x(), vector3f.y(), vector3f.z());
|
||||
}
|
||||
}
|
||||
@@ -1,41 +1,21 @@
|
||||
--- a/com/mojang/blaze3d/vertex/VertexFormat.java
|
||||
+++ b/com/mojang/blaze3d/vertex/VertexFormat.java
|
||||
@@ -13,7 +_,7 @@
|
||||
@@ -12,7 +_,7 @@
|
||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
-public class VertexFormat {
|
||||
+public class VertexFormat implements net.minecraftforge.client.extensions.IForgeVertexFormat {
|
||||
public static final int UNKNOWN_ELEMENT = -1;
|
||||
private final List<VertexFormatElement> elements;
|
||||
private final List<String> names;
|
||||
@@ -22,6 +_,7 @@
|
||||
private final int[] offsetsByElement = new int[32];
|
||||
@Nullable
|
||||
private VertexBuffer immediateDrawVertexBuffer;
|
||||
+ private final com.google.common.collect.ImmutableMap<String, VertexFormatElement> elementMapping;
|
||||
|
||||
VertexFormat(List<VertexFormatElement> p_343616_, List<String> p_345241_, IntList p_345522_, int p_344162_) {
|
||||
this.elements = p_343616_;
|
||||
@@ -34,6 +_,11 @@
|
||||
int j = vertexformatelement != null ? p_343616_.indexOf(vertexformatelement) : -1;
|
||||
this.offsetsByElement[i] = j != -1 ? p_345522_.getInt(j) : -1;
|
||||
}
|
||||
+
|
||||
+ ImmutableMap.Builder<String, VertexFormatElement> elements = ImmutableMap.builder();
|
||||
+ for (int i = 0; i < p_343616_.size(); i++)
|
||||
+ elements.put(p_345241_.get(i), p_343616_.get(i));
|
||||
+ this.elementMapping = elements.buildOrThrow();
|
||||
}
|
||||
|
||||
public static VertexFormat.Builder builder() {
|
||||
@@ -158,6 +_,9 @@
|
||||
private final ImmutableList<VertexFormatElement> elements;
|
||||
private final ImmutableMap<String, VertexFormatElement> elementMapping;
|
||||
private final IntList offsets = new IntArrayList();
|
||||
@@ -113,6 +_,9 @@
|
||||
|
||||
return vertexbuffer;
|
||||
}
|
||||
+
|
||||
+ public ImmutableMap<String, VertexFormatElement> getElementMapping() { return elementMapping; }
|
||||
+ public int getOffset(int index) { return offsetsByElement[index]; }
|
||||
+ public int getOffset(int index) { return offsets.getInt(index); }
|
||||
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
public static class Builder {
|
||||
public static enum IndexType {
|
||||
@@ -1,6 +1,6 @@
|
||||
--- a/net/minecraft/CrashReport.java
|
||||
+++ b/net/minecraft/CrashReport.java
|
||||
@@ -58,14 +_,9 @@
|
||||
@@ -59,14 +_,9 @@
|
||||
if (this.uncategorizedStackTrace != null && this.uncategorizedStackTrace.length > 0) {
|
||||
p_127520_.append("-- Head --\n");
|
||||
p_127520_.append("Thread: ").append(Thread.currentThread().getName()).append("\n");
|
||||
@@ -18,7 +18,7 @@
|
||||
}
|
||||
|
||||
for (CrashReportCategory crashreportcategory : this.details) {
|
||||
@@ -73,6 +_,7 @@
|
||||
@@ -74,6 +_,7 @@
|
||||
p_127520_.append("\n\n");
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
this.systemReport.appendToCrashReportString(p_127520_);
|
||||
}
|
||||
|
||||
@@ -92,18 +_,7 @@
|
||||
@@ -93,18 +_,7 @@
|
||||
throwable.setStackTrace(this.exception.getStackTrace());
|
||||
}
|
||||
|
||||
@@ -45,4 +45,4 @@
|
||||
+ return net.minecraftforge.logging.CrashReportExtender.generateEnhancedStackTrace(throwable);
|
||||
}
|
||||
|
||||
public String getFriendlyReport(ReportType p_343869_, List<String> p_342487_) {
|
||||
public String getFriendlyReport() {
|
||||
@@ -1,6 +1,6 @@
|
||||
--- a/net/minecraft/SharedConstants.java
|
||||
+++ b/net/minecraft/SharedConstants.java
|
||||
@@ -171,6 +_,7 @@
|
||||
@@ -181,6 +_,7 @@
|
||||
}
|
||||
|
||||
static {
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
}
|
||||
|
||||
public AdvancementHolder save(Consumer<AdvancementHolder> p_138390_, String p_138391_) {
|
||||
- AdvancementHolder advancementholder = this.build(ResourceLocation.parse(p_138391_));
|
||||
+ return save(p_138390_, ResourceLocation.parse(p_138391_));
|
||||
- AdvancementHolder advancementholder = this.build(new ResourceLocation(p_138391_));
|
||||
+ return save(p_138390_, new ResourceLocation(p_138391_));
|
||||
+ }
|
||||
+
|
||||
+ public AdvancementHolder save(Consumer<AdvancementHolder> p_138390_, ResourceLocation id) {
|
||||
|
||||
@@ -1,24 +1,12 @@
|
||||
--- a/net/minecraft/client/Camera.java
|
||||
+++ b/net/minecraft/client/Camera.java
|
||||
@@ -105,9 +_,13 @@
|
||||
@@ -227,6 +_,18 @@
|
||||
return this.partialTickTime;
|
||||
}
|
||||
|
||||
protected void setRotation(float p_90573_, float p_90574_) {
|
||||
+ setRotation(p_90573_, p_90574_, 0.0f);
|
||||
+ }
|
||||
+
|
||||
+ public void setRotation(float p_90573_, float p_90574_, float z) {
|
||||
this.xRot = p_90574_;
|
||||
this.yRot = p_90573_;
|
||||
- this.rotation.rotationYXZ((float) Math.PI - p_90573_ * (float) (Math.PI / 180.0), -p_90574_ * (float) (Math.PI / 180.0), 0.0F);
|
||||
+ this.rotation.rotationYXZ((float) Math.PI - p_90573_ * (float) (Math.PI / 180.0), -p_90574_ * (float) (Math.PI / 180.0), z * (float) (Math.PI / 180.0));
|
||||
FORWARDS.rotate(this.rotation, this.forwards);
|
||||
UP.rotate(this.rotation, this.up);
|
||||
LEFT.rotate(this.rotation, this.left);
|
||||
@@ -223,6 +_,13 @@
|
||||
|
||||
public float getPartialTickTime() {
|
||||
return this.partialTickTime;
|
||||
+ public void setAnglesInternal(float yaw, float pitch) {
|
||||
+ this.yRot = yaw;
|
||||
+ this.xRot = pitch;
|
||||
+ }
|
||||
+
|
||||
+ public net.minecraft.world.level.block.state.BlockState getBlockAtCamera() {
|
||||
@@ -26,6 +14,8 @@
|
||||
+ return net.minecraft.world.level.block.Blocks.AIR.defaultBlockState();
|
||||
+ else
|
||||
+ return this.level.getBlockState(this.blockPosition).getStateAtViewpoint(this.level, this.blockPosition, this.position);
|
||||
}
|
||||
|
||||
+ }
|
||||
+
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
public static class NearPlane {
|
||||
final Vec3 forward;
|
||||
@@ -5,6 +5,6 @@
|
||||
@DontObfuscate
|
||||
public static String getClientModName() {
|
||||
- return "vanilla";
|
||||
+ return net.minecraftforge.internal.BrandingControl.getBranding();
|
||||
+ return net.minecraftforge.internal.BrandingControl.getClientBranding();
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
--- a/net/minecraft/client/DeltaTracker.java
|
||||
+++ b/net/minecraft/client/DeltaTracker.java
|
||||
@@ -87,18 +_,22 @@
|
||||
|
||||
private void pause() {
|
||||
if (!this.paused) {
|
||||
+ if (net.minecraftforge.client.ForgeHooksClient.onClientPauseChangePre(true)) return;
|
||||
this.pausedDeltaTickResidual = this.deltaTickResidual;
|
||||
}
|
||||
|
||||
this.paused = true;
|
||||
+ net.minecraftforge.client.ForgeHooksClient.onClientPauseChangePost(true);
|
||||
}
|
||||
|
||||
private void unPause() {
|
||||
if (this.paused) {
|
||||
+ if (net.minecraftforge.client.ForgeHooksClient.onClientPauseChangePre(false)) return;
|
||||
this.deltaTickResidual = this.pausedDeltaTickResidual;
|
||||
}
|
||||
|
||||
this.paused = false;
|
||||
+ net.minecraftforge.client.ForgeHooksClient.onClientPauseChangePost(false);
|
||||
}
|
||||
|
||||
public void updateFrozenState(boolean p_344005_) {
|
||||
@@ -1,6 +1,6 @@
|
||||
--- a/net/minecraft/client/KeyboardHandler.java
|
||||
+++ b/net/minecraft/client/KeyboardHandler.java
|
||||
@@ -405,9 +_,9 @@
|
||||
@@ -410,9 +_,9 @@
|
||||
Screen.wrapScreenError(() -> {
|
||||
if (p_90897_ == 1 || p_90897_ == 2) {
|
||||
screen.afterKeyboardAction();
|
||||
@@ -12,7 +12,7 @@
|
||||
}
|
||||
}, "keyPressed event handler", screen.getClass().getCanonicalName());
|
||||
if (aboolean[0]) {
|
||||
@@ -476,20 +_,21 @@
|
||||
@@ -481,20 +_,21 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
--- a/net/minecraft/client/Minecraft.java
|
||||
+++ b/net/minecraft/client/Minecraft.java
|
||||
@@ -254,7 +_,7 @@
|
||||
@@ -259,7 +_,7 @@
|
||||
import org.slf4j.Logger;
|
||||
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
@@ -9,7 +9,7 @@
|
||||
static Minecraft instance;
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
public static final boolean ON_OSX = Util.getPlatform() == Util.OS.OSX;
|
||||
@@ -431,7 +_,6 @@
|
||||
@@ -438,7 +_,6 @@
|
||||
}
|
||||
}, Util.nonCriticalIoPool());
|
||||
LOGGER.info("Setting user: {}", this.user.getName());
|
||||
@@ -17,7 +17,7 @@
|
||||
this.demo = p_91084_.game.demo;
|
||||
this.allowsMultiplayer = !p_91084_.game.disableMultiplayer;
|
||||
this.allowsChat = !p_91084_.game.disableChat;
|
||||
@@ -468,15 +_,15 @@
|
||||
@@ -475,15 +_,15 @@
|
||||
}
|
||||
|
||||
this.window.setFramerateLimit(this.options.framerateLimit().get());
|
||||
@@ -34,8 +34,8 @@
|
||||
+ net.minecraftforge.client.loading.ClientModLoader.begin(this, this.resourcePackRepository, this.resourceManager);
|
||||
this.resourcePackRepository.reload();
|
||||
this.options.loadSelectedResourcePacks(this.resourcePackRepository);
|
||||
this.languageManager = new LanguageManager(this.options.languageCode, p_340763_ -> {
|
||||
@@ -540,6 +_,7 @@
|
||||
this.languageManager = new LanguageManager(this.options.languageCode);
|
||||
@@ -542,6 +_,7 @@
|
||||
this.entityRenderDispatcher = new EntityRenderDispatcher(this, this.textureManager, this.itemRenderer, this.blockRenderer, this.font, this.options, this.entityModels);
|
||||
this.resourceManager.registerReloadListener(this.entityRenderDispatcher);
|
||||
this.particleEngine = new ParticleEngine(this.level, this.textureManager);
|
||||
@@ -43,12 +43,16 @@
|
||||
this.resourceManager.registerReloadListener(this.particleEngine);
|
||||
this.paintingTextures = new PaintingTextureManager(this.textureManager);
|
||||
this.resourceManager.registerReloadListener(this.paintingTextures);
|
||||
@@ -552,11 +_,15 @@
|
||||
@@ -554,13 +_,18 @@
|
||||
this.gameRenderer = new GameRenderer(this, this.entityRenderDispatcher.getItemInHandRenderer(), this.resourceManager, this.renderBuffers);
|
||||
this.resourceManager.registerReloadListener(this.gameRenderer.createReloadListener());
|
||||
this.levelRenderer = new LevelRenderer(this, this.entityRenderDispatcher, this.blockEntityRenderDispatcher, this.renderBuffers);
|
||||
+ net.minecraftforge.fml.ModLoader.get().postEvent(new net.minecraftforge.client.event.RenderLevelStageEvent.RegisterStageEvent());
|
||||
this.resourceManager.registerReloadListener(this.levelRenderer);
|
||||
- this.createSearchTrees();
|
||||
+ // Forge: We delay this to allow registration of modded creative mode tabs
|
||||
+ //this.createSearchTrees();
|
||||
this.resourceManager.registerReloadListener(this.searchRegistry);
|
||||
this.gpuWarnlistManager = new GpuWarnlistManager();
|
||||
this.resourceManager.registerReloadListener(this.gpuWarnlistManager);
|
||||
this.resourceManager.registerReloadListener(this.regionalCompliancies);
|
||||
@@ -59,7 +63,7 @@
|
||||
this.debugRenderer = new DebugRenderer(this);
|
||||
RealmsClient realmsclient = RealmsClient.create(this);
|
||||
this.realmsDataFetcher = new RealmsDataFetcher(realmsclient);
|
||||
@@ -580,6 +_,7 @@
|
||||
@@ -584,6 +_,7 @@
|
||||
this.options.fullscreen().set(this.window.isFullscreen());
|
||||
}
|
||||
|
||||
@@ -67,7 +71,7 @@
|
||||
this.window.updateVsync(this.options.enableVsync().get());
|
||||
this.window.updateRawMouseInput(this.options.rawMouseInput().get());
|
||||
this.window.setDefaultErrorCallback();
|
||||
@@ -600,16 +_,17 @@
|
||||
@@ -604,16 +_,17 @@
|
||||
GameLoadTimesEvent.INSTANCE.beginStep(TelemetryProperty.LOAD_TIME_LOADING_OVERLAY_MS);
|
||||
Minecraft.GameLoadCookie minecraft$gameloadcookie = new Minecraft.GameLoadCookie(realmsclient, p_91084_.quickPlay);
|
||||
this.setOverlay(
|
||||
@@ -88,7 +92,7 @@
|
||||
);
|
||||
this.quickPlayLog = QuickPlayLog.of(p_91084_.quickPlay.path());
|
||||
}
|
||||
@@ -698,6 +_,7 @@
|
||||
@@ -702,6 +_,7 @@
|
||||
StringBuilder stringbuilder = new StringBuilder("Minecraft");
|
||||
if (checkModStatus().shouldReportAsModified()) {
|
||||
stringbuilder.append("*");
|
||||
@@ -96,7 +100,7 @@
|
||||
}
|
||||
|
||||
stringbuilder.append(" ");
|
||||
@@ -721,6 +_,8 @@
|
||||
@@ -725,6 +_,8 @@
|
||||
}
|
||||
|
||||
private UserApiService createUserApiService(YggdrasilAuthenticationService p_193586_, GameConfig p_193587_) {
|
||||
@@ -105,7 +109,7 @@
|
||||
return p_193587_.user.user.getType() != User.Type.MSA
|
||||
? UserApiService.OFFLINE
|
||||
: p_193586_.createUserApiService(p_193587_.user.user.getAccessToken());
|
||||
@@ -731,7 +_,7 @@
|
||||
@@ -735,7 +_,7 @@
|
||||
}
|
||||
|
||||
private void rollbackResourcePacks(Throwable p_91240_, @Nullable Minecraft.GameLoadCookie p_299515_) {
|
||||
@@ -114,7 +118,48 @@
|
||||
this.clearResourcePacksOnError(p_91240_, null, p_299515_);
|
||||
} else {
|
||||
Util.throwAsRuntime(p_91240_);
|
||||
@@ -991,12 +_,6 @@
|
||||
@@ -817,9 +_,12 @@
|
||||
}
|
||||
|
||||
public void createSearchTrees() {
|
||||
+ var nameSearchKeys = net.minecraftforge.client.CreativeModeTabSearchRegistry.getNameSearchKeys();
|
||||
+ var tagSearchKeys = net.minecraftforge.client.CreativeModeTabSearchRegistry.getTagSearchKeys();
|
||||
+ for (var nameSearchKey : nameSearchKeys.values())
|
||||
this.searchRegistry
|
||||
.register(
|
||||
- SearchRegistry.CREATIVE_NAMES,
|
||||
+ nameSearchKey,
|
||||
p_231451_ -> new FullTextSearchTree<>(
|
||||
p_325272_ -> p_325272_.getTooltipLines(Item.TooltipContext.EMPTY, null, TooltipFlag.Default.NORMAL.asCreative())
|
||||
.stream()
|
||||
@@ -829,8 +_,9 @@
|
||||
p_231451_
|
||||
)
|
||||
);
|
||||
+ for (var tagSearchKey : tagSearchKeys.values())
|
||||
this.searchRegistry
|
||||
- .register(SearchRegistry.CREATIVE_TAGS, p_231430_ -> new IdSearchTree<>(p_231353_ -> p_231353_.getTags().map(TagKey::location), p_231430_));
|
||||
+ .register(tagSearchKey, p_231430_ -> new IdSearchTree<>(p_231353_ -> p_231353_.getTags().map(TagKey::location), p_231430_));
|
||||
this.searchRegistry
|
||||
.register(
|
||||
SearchRegistry.RECIPE_COLLECTIONS,
|
||||
@@ -854,9 +_,12 @@
|
||||
p_301514_
|
||||
)
|
||||
);
|
||||
- CreativeModeTabs.searchTab().setSearchTreeBuilder(p_255439_ -> {
|
||||
- this.populateSearchTree(SearchRegistry.CREATIVE_NAMES, p_255439_);
|
||||
- this.populateSearchTree(SearchRegistry.CREATIVE_TAGS, p_255439_);
|
||||
+ nameSearchKeys.forEach((tab, nameSearchKey) -> {
|
||||
+ var tagSearchKey = tagSearchKeys.get(tab);
|
||||
+ tab.setSearchTreeBuilder(contents -> {
|
||||
+ this.populateSearchTree(nameSearchKey, contents);
|
||||
+ this.populateSearchTree(tagSearchKey, contents);
|
||||
+ });
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1039,12 +_,6 @@
|
||||
LOGGER.error("setScreen called from non-game thread");
|
||||
}
|
||||
|
||||
@@ -127,7 +172,7 @@
|
||||
if (p_91153_ == null && this.clientLevelTeardownInProgress) {
|
||||
throw new IllegalStateException("Trying to return to in-game GUI during disconnection");
|
||||
} else {
|
||||
@@ -1010,6 +_,23 @@
|
||||
@@ -1058,6 +_,23 @@
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,27 +196,37 @@
|
||||
this.screen = p_91153_;
|
||||
if (this.screen != null) {
|
||||
this.screen.added();
|
||||
@@ -1088,6 +_,7 @@
|
||||
this.resourceManager.close();
|
||||
FreeTypeUtil.destroy();
|
||||
Util.shutdownExecutors();
|
||||
+ net.minecraftforge.fml.config.ConfigTracker.INSTANCE.forceUnload();
|
||||
} catch (Throwable throwable) {
|
||||
LOGGER.error("Shutdown failure!", throwable);
|
||||
throw throwable;
|
||||
@@ -1155,9 +_,11 @@
|
||||
@@ -1204,9 +_,12 @@
|
||||
this.mouseHandler.handleAccumulatedMovement();
|
||||
this.profiler.pop();
|
||||
if (!this.noRender) {
|
||||
+ net.minecraftforge.client.event.ForgeEventFactoryClient.onRenderTickStart(this.timer);
|
||||
+ this.realPartialTick = this.pause ? this.pausePartialTick : this.timer.partialTick; // Cache this since pause is volatile
|
||||
+ net.minecraftforge.event.ForgeEventFactory.onRenderTickStart(this.realPartialTick);
|
||||
this.profiler.popPush("gameRenderer");
|
||||
this.gameRenderer.render(this.timer, p_91384_);
|
||||
this.gameRenderer.render(this.pause ? this.pausePartialTick : this.timer.partialTick, i, p_91384_);
|
||||
this.profiler.pop();
|
||||
+ net.minecraftforge.client.event.ForgeEventFactoryClient.onRenderTickEnd(this.timer);
|
||||
+ net.minecraftforge.event.ForgeEventFactory.onRenderTickEnd(this.realPartialTick);
|
||||
}
|
||||
|
||||
if (this.fpsPieResults != null) {
|
||||
@@ -1283,10 +_,12 @@
|
||||
@@ -1240,7 +_,7 @@
|
||||
boolean flag1 = this.hasSingleplayerServer()
|
||||
&& (this.screen != null && this.screen.isPauseScreen() || this.overlay != null && this.overlay.isPauseScreen())
|
||||
&& !this.singleplayerServer.isPublished();
|
||||
- if (this.pause != flag1) {
|
||||
+ if (this.pause != flag1 && !net.minecraftforge.client.ForgeHooksClient.onClientPauseChangePre(flag1)) {
|
||||
if (flag1) {
|
||||
this.pausePartialTick = this.timer.partialTick;
|
||||
} else {
|
||||
@@ -1248,6 +_,7 @@
|
||||
}
|
||||
|
||||
this.pause = flag1;
|
||||
+ net.minecraftforge.client.ForgeHooksClient.onClientPauseChangePost(flag1);
|
||||
}
|
||||
|
||||
long l = Util.getNanos();
|
||||
@@ -1340,10 +_,12 @@
|
||||
this.window.setGuiScale((double)i);
|
||||
if (this.screen != null) {
|
||||
this.screen.resize(this, this.window.getGuiScaledWidth(), this.window.getGuiScaledHeight());
|
||||
@@ -184,7 +239,7 @@
|
||||
this.gameRenderer.resize(this.window.getWidth(), this.window.getHeight());
|
||||
this.mouseHandler.setIgnoreFirstMove();
|
||||
}
|
||||
@@ -1554,6 +_,7 @@
|
||||
@@ -1619,6 +_,7 @@
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
@@ -192,7 +247,7 @@
|
||||
this.running = false;
|
||||
}
|
||||
|
||||
@@ -1582,10 +_,18 @@
|
||||
@@ -1647,10 +_,18 @@
|
||||
if (p_91387_ && this.hitResult != null && this.hitResult.getType() == HitResult.Type.BLOCK) {
|
||||
BlockHitResult blockhitresult = (BlockHitResult)this.hitResult;
|
||||
BlockPos blockpos = blockhitresult.getBlockPos();
|
||||
@@ -214,7 +269,7 @@
|
||||
this.player.swing(InteractionHand.MAIN_HAND);
|
||||
}
|
||||
}
|
||||
@@ -1613,6 +_,8 @@
|
||||
@@ -1678,6 +_,8 @@
|
||||
return false;
|
||||
} else {
|
||||
boolean flag = false;
|
||||
@@ -223,7 +278,7 @@
|
||||
switch (this.hitResult.getType()) {
|
||||
case ENTITY:
|
||||
this.gameMode.attack(this.player, ((EntityHitResult)this.hitResult).getEntity());
|
||||
@@ -1620,7 +_,7 @@
|
||||
@@ -1685,7 +_,7 @@
|
||||
case BLOCK:
|
||||
BlockHitResult blockhitresult = (BlockHitResult)this.hitResult;
|
||||
BlockPos blockpos = blockhitresult.getBlockPos();
|
||||
@@ -232,7 +287,7 @@
|
||||
this.gameMode.startDestroyBlock(blockpos, blockhitresult.getDirection());
|
||||
if (this.level.getBlockState(blockpos).isAir()) {
|
||||
flag = true;
|
||||
@@ -1633,8 +_,10 @@
|
||||
@@ -1698,8 +_,10 @@
|
||||
}
|
||||
|
||||
this.player.resetAttackStrengthTicker();
|
||||
@@ -243,7 +298,7 @@
|
||||
this.player.swing(InteractionHand.MAIN_HAND);
|
||||
return flag;
|
||||
}
|
||||
@@ -1650,6 +_,12 @@
|
||||
@@ -1715,6 +_,12 @@
|
||||
}
|
||||
|
||||
for (InteractionHand interactionhand : InteractionHand.values()) {
|
||||
@@ -256,7 +311,7 @@
|
||||
ItemStack itemstack = this.player.getItemInHand(interactionhand);
|
||||
if (!itemstack.isItemEnabled(this.level.enabledFeatures())) {
|
||||
return;
|
||||
@@ -1670,7 +_,7 @@
|
||||
@@ -1735,7 +_,7 @@
|
||||
}
|
||||
|
||||
if (interactionresult.consumesAction()) {
|
||||
@@ -265,7 +320,7 @@
|
||||
this.player.swing(interactionhand);
|
||||
}
|
||||
|
||||
@@ -1682,7 +_,7 @@
|
||||
@@ -1747,7 +_,7 @@
|
||||
int i = itemstack.getCount();
|
||||
InteractionResult interactionresult1 = this.gameMode.useItemOn(this.player, interactionhand, blockhitresult);
|
||||
if (interactionresult1.consumesAction()) {
|
||||
@@ -274,7 +329,7 @@
|
||||
this.player.swing(interactionhand);
|
||||
if (!itemstack.isEmpty() && (itemstack.getCount() != i || this.gameMode.hasInfiniteItems())) {
|
||||
this.gameRenderer.itemInHandRenderer.itemUsed(interactionhand);
|
||||
@@ -1698,6 +_,9 @@
|
||||
@@ -1763,6 +_,9 @@
|
||||
}
|
||||
}
|
||||
|
||||
@@ -284,7 +339,7 @@
|
||||
if (!itemstack.isEmpty()) {
|
||||
InteractionResult interactionresult2 = this.gameMode.useItem(this.player, interactionhand);
|
||||
if (interactionresult2.consumesAction()) {
|
||||
@@ -1728,6 +_,8 @@
|
||||
@@ -1793,6 +_,8 @@
|
||||
this.rightClickDelay--;
|
||||
}
|
||||
|
||||
@@ -293,7 +348,7 @@
|
||||
this.profiler.push("gui");
|
||||
this.chatListener.tick();
|
||||
this.gui.tick(this.pause);
|
||||
@@ -1811,6 +_,7 @@
|
||||
@@ -1877,6 +_,7 @@
|
||||
|
||||
this.tutorial.tick();
|
||||
|
||||
@@ -301,7 +356,7 @@
|
||||
try {
|
||||
this.level.tick(() -> true);
|
||||
} catch (Throwable throwable) {
|
||||
@@ -1824,6 +_,7 @@
|
||||
@@ -1890,6 +_,7 @@
|
||||
|
||||
throw new ReportedException(crashreport);
|
||||
}
|
||||
@@ -309,15 +364,15 @@
|
||||
}
|
||||
|
||||
this.profiler.popPush("animateTick");
|
||||
@@ -1843,6 +_,7 @@
|
||||
@@ -1909,6 +_,7 @@
|
||||
this.profiler.popPush("keyboard");
|
||||
this.keyboardHandler.tick();
|
||||
this.profiler.pop();
|
||||
+ net.minecraftforge.event.ForgeEventFactory.onPostClientTick();
|
||||
}
|
||||
|
||||
private boolean isLevelRunningNormally() {
|
||||
@@ -2043,6 +_,7 @@
|
||||
private boolean isMultiplayerServer() {
|
||||
@@ -2105,6 +_,7 @@
|
||||
}
|
||||
|
||||
public void setLevel(ClientLevel p_91157_, ReceivingLevelScreen.Reason p_335893_) {
|
||||
@@ -325,7 +380,7 @@
|
||||
this.updateScreenAndTick(new ReceivingLevelScreen(() -> false, p_335893_));
|
||||
this.level = p_91157_;
|
||||
this.updateLevelInEngines(p_91157_);
|
||||
@@ -2080,6 +_,7 @@
|
||||
@@ -2142,6 +_,7 @@
|
||||
IntegratedServer integratedserver = this.singleplayerServer;
|
||||
this.singleplayerServer = null;
|
||||
this.gameRenderer.resetData();
|
||||
@@ -333,7 +388,7 @@
|
||||
this.gameMode = null;
|
||||
this.narrator.clear();
|
||||
this.clientLevelTeardownInProgress = true;
|
||||
@@ -2087,6 +_,7 @@
|
||||
@@ -2149,6 +_,7 @@
|
||||
try {
|
||||
this.updateScreenAndTick(p_335030_);
|
||||
if (this.level != null) {
|
||||
@@ -341,7 +396,7 @@
|
||||
if (integratedserver != null) {
|
||||
this.profiler.push("waitForServer");
|
||||
|
||||
@@ -2099,6 +_,7 @@
|
||||
@@ -2161,6 +_,7 @@
|
||||
|
||||
this.gui.onDisconnected();
|
||||
this.isLocalServer = false;
|
||||
@@ -349,7 +404,7 @@
|
||||
}
|
||||
|
||||
this.level = null;
|
||||
@@ -2245,6 +_,7 @@
|
||||
@@ -2307,6 +_,7 @@
|
||||
|
||||
private void pickBlock() {
|
||||
if (this.hitResult != null && this.hitResult.getType() != HitResult.Type.MISS) {
|
||||
@@ -357,7 +412,7 @@
|
||||
boolean flag = this.player.getAbilities().instabuild;
|
||||
BlockEntity blockentity = null;
|
||||
HitResult.Type hitresult$type = this.hitResult.getType();
|
||||
@@ -2257,7 +_,7 @@
|
||||
@@ -2319,7 +_,7 @@
|
||||
}
|
||||
|
||||
Block block = blockstate.getBlock();
|
||||
@@ -366,7 +421,7 @@
|
||||
if (itemstack.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
@@ -2271,7 +_,7 @@
|
||||
@@ -2333,7 +_,7 @@
|
||||
}
|
||||
|
||||
Entity entity = ((EntityHitResult)this.hitResult).getEntity();
|
||||
@@ -375,7 +430,7 @@
|
||||
if (itemstack == null) {
|
||||
return;
|
||||
}
|
||||
@@ -2855,6 +_,10 @@
|
||||
@@ -2931,6 +_,19 @@
|
||||
@Nullable
|
||||
public static String getLauncherBrand() {
|
||||
return System.getProperty("minecraft.launcher.brand");
|
||||
@@ -383,6 +438,15 @@
|
||||
+
|
||||
+ public ItemColors getItemColors() {
|
||||
+ return this.itemColors;
|
||||
+ }
|
||||
+
|
||||
+ public SearchRegistry getSearchTreeManager() {
|
||||
+ return this.searchRegistry;
|
||||
+ }
|
||||
+
|
||||
+ private float realPartialTick;
|
||||
+ public float getPartialTick() {
|
||||
+ return this.realPartialTick;
|
||||
}
|
||||
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
@@ -1,6 +1,6 @@
|
||||
--- a/net/minecraft/client/MouseHandler.java
|
||||
+++ b/net/minecraft/client/MouseHandler.java
|
||||
@@ -79,6 +_,7 @@
|
||||
@@ -74,6 +_,7 @@
|
||||
this.activeButton = -1;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
boolean[] aboolean = new boolean[]{false};
|
||||
if (this.minecraft.getOverlay() == null) {
|
||||
if (this.minecraft.screen == null) {
|
||||
@@ -91,9 +_,9 @@
|
||||
@@ -86,9 +_,9 @@
|
||||
Screen screen = this.minecraft.screen;
|
||||
if (flag) {
|
||||
screen.afterMouseAction();
|
||||
@@ -20,7 +20,7 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -116,6 +_,7 @@
|
||||
@@ -111,6 +_,7 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -28,7 +28,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,7 +_,9 @@
|
||||
@@ -124,7 +_,9 @@
|
||||
if (this.minecraft.screen != null) {
|
||||
double d3 = this.xpos * (double)this.minecraft.getWindow().getGuiScaledWidth() / (double)this.minecraft.getWindow().getScreenWidth();
|
||||
double d4 = this.ypos * (double)this.minecraft.getWindow().getGuiScaledHeight() / (double)this.minecraft.getWindow().getScreenHeight();
|
||||
@@ -39,7 +39,7 @@
|
||||
this.minecraft.screen.afterMouseAction();
|
||||
} else if (this.minecraft.player != null) {
|
||||
if (this.accumulatedScrollX != 0.0 && Math.signum(d1) != Math.signum(this.accumulatedScrollX)) {
|
||||
@@ -151,6 +_,7 @@
|
||||
@@ -146,6 +_,7 @@
|
||||
this.accumulatedScrollX -= (double)j;
|
||||
this.accumulatedScrollY -= (double)i;
|
||||
int k = i == 0 ? -j : i;
|
||||
@@ -47,7 +47,7 @@
|
||||
if (this.minecraft.player.isSpectator()) {
|
||||
if (this.minecraft.gui.getSpectatorGui().isMenuActive()) {
|
||||
this.minecraft.gui.getSpectatorGui().onMouseScrolled(-k);
|
||||
@@ -236,7 +_,7 @@
|
||||
@@ -216,7 +_,7 @@
|
||||
if (this.activeButton != -1 && this.mousePressedTime > 0.0) {
|
||||
double d4 = this.accumulatedDX * (double)this.minecraft.getWindow().getGuiScaledWidth() / (double)this.minecraft.getWindow().getScreenWidth();
|
||||
double d5 = this.accumulatedDY * (double)this.minecraft.getWindow().getGuiScaledHeight() / (double)this.minecraft.getWindow().getScreenHeight();
|
||||
@@ -56,7 +56,7 @@
|
||||
}
|
||||
|
||||
screen.afterMouseMove();
|
||||
@@ -303,6 +_,14 @@
|
||||
@@ -283,6 +_,14 @@
|
||||
|
||||
public double ypos() {
|
||||
return this.ypos;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
--- a/net/minecraft/client/Options.java
|
||||
+++ b/net/minecraft/client/Options.java
|
||||
@@ -681,7 +_,8 @@
|
||||
@@ -680,7 +_,8 @@
|
||||
},
|
||||
new OptionInstance.LazyEnum<>(
|
||||
() -> Stream.concat(Stream.of(""), Minecraft.getInstance().getSoundManager().getAvailableSoundDevices().stream()).toList(),
|
||||
@@ -10,7 +10,7 @@
|
||||
? Optional.empty()
|
||||
: Optional.of(p_232011_),
|
||||
Codec.STRING
|
||||
@@ -1050,6 +_,7 @@
|
||||
@@ -1044,6 +_,7 @@
|
||||
}
|
||||
|
||||
public Options(Minecraft p_92138_, File p_92139_) {
|
||||
@@ -18,7 +18,7 @@
|
||||
this.minecraft = p_92138_;
|
||||
this.optionsFile = new File(p_92139_, "options.txt");
|
||||
boolean flag = Runtime.getRuntime().maxMemory() >= 1000000000L;
|
||||
@@ -1184,12 +_,22 @@
|
||||
@@ -1178,12 +_,22 @@
|
||||
p_168428_.process("telemetryOptInExtra", this.telemetryOptInExtra);
|
||||
this.onboardAccessibility = p_168428_.process("onboardAccessibility", this.onboardAccessibility);
|
||||
p_168428_.process("menuBackgroundBlurriness", this.menuBackgroundBlurriness);
|
||||
@@ -42,7 +42,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1207,6 +_,10 @@
|
||||
@@ -1201,6 +_,10 @@
|
||||
}
|
||||
|
||||
public void load() {
|
||||
@@ -53,7 +53,7 @@
|
||||
try {
|
||||
if (!this.optionsFile.exists()) {
|
||||
return;
|
||||
@@ -1234,7 +_,8 @@
|
||||
@@ -1228,7 +_,8 @@
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@
|
||||
new Options.FieldAccess() {
|
||||
@Nullable
|
||||
private String getValueOrNull(String p_168459_) {
|
||||
@@ -1428,6 +_,7 @@
|
||||
@@ -1422,6 +_,7 @@
|
||||
}
|
||||
|
||||
public void broadcastOptions() {
|
||||
@@ -71,7 +71,7 @@
|
||||
if (this.minecraft.player != null) {
|
||||
this.minecraft.player.connection.send(new ServerboundClientInformationPacket(this.buildPlayerInformation()));
|
||||
}
|
||||
@@ -1546,6 +_,23 @@
|
||||
@@ -1544,6 +_,23 @@
|
||||
|
||||
public static Component genericValueLabel(Component p_231901_, int p_231902_) {
|
||||
return genericValueLabel(p_231901_, Component.literal(Integer.toString(p_231902_)));
|
||||
@@ -94,4 +94,4 @@
|
||||
+ keySmoothCamera.setKeyConflictContext(inGame);
|
||||
}
|
||||
|
||||
public static Component genericValueOrOffLabel(Component p_345288_, int p_344826_) {
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
@@ -9,7 +9,7 @@
|
||||
private final SubtitleOverlay subtitleOverlay;
|
||||
private final SpectatorGui spectatorGui;
|
||||
private final PlayerTabOverlay tabList;
|
||||
@@ -318,7 +_,7 @@
|
||||
@@ -320,7 +_,7 @@
|
||||
Window window = this.minecraft.getWindow();
|
||||
int i = Mth.floor(this.minecraft.mouseHandler.xpos() * (double)window.getGuiScaledWidth() / (double)window.getScreenWidth());
|
||||
int j = Mth.floor(this.minecraft.mouseHandler.ypos() * (double)window.getGuiScaledHeight() / (double)window.getScreenHeight());
|
||||
@@ -18,7 +18,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
@@ -434,6 +_,8 @@
|
||||
@@ -444,6 +_,8 @@
|
||||
|
||||
for (MobEffectInstance mobeffectinstance : Ordering.natural().reverse().sortedCopy(collection)) {
|
||||
Holder<MobEffect> holder = mobeffectinstance.getEffect();
|
||||
@@ -27,7 +27,7 @@
|
||||
if (mobeffectinstance.showIcon()) {
|
||||
int i = p_282812_.guiWidth();
|
||||
int j = 1;
|
||||
@@ -463,6 +_,7 @@
|
||||
@@ -473,6 +_,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
TextureAtlasSprite textureatlassprite = mobeffecttexturemanager.get(holder);
|
||||
int l1 = i;
|
||||
int i1 = j;
|
||||
@@ -625,6 +_,10 @@
|
||||
@@ -635,6 +_,10 @@
|
||||
}
|
||||
|
||||
private void renderSelectedItemName(GuiGraphics p_283501_) {
|
||||
@@ -46,7 +46,7 @@
|
||||
this.minecraft.getProfiler().push("selectedItemName");
|
||||
if (this.toolHighlightTimer > 0 && !this.lastToolHighlight.isEmpty()) {
|
||||
MutableComponent mutablecomponent = Component.empty().append(this.lastToolHighlight.getHoverName()).withStyle(this.lastToolHighlight.getRarity().color());
|
||||
@@ -632,9 +_,13 @@
|
||||
@@ -642,9 +_,13 @@
|
||||
mutablecomponent.withStyle(ChatFormatting.ITALIC);
|
||||
}
|
||||
|
||||
@@ -62,16 +62,16 @@
|
||||
if (!this.minecraft.gameMode.canHurtPlayer()) {
|
||||
k += 14;
|
||||
}
|
||||
@@ -645,7 +_,7 @@
|
||||
}
|
||||
@@ -656,7 +_,7 @@
|
||||
|
||||
if (l > 0) {
|
||||
- p_283501_.drawStringWithBackdrop(this.getFont(), mutablecomponent, j, k, i, FastColor.ARGB32.color(l, -1));
|
||||
+ p_283501_.drawStringWithBackdrop(font, highlightTip, j, k, i, FastColor.ARGB32.color(l, -1));
|
||||
p_283501_.fill(j - 2, k - 2, j + i + 2, k + 9 + 2, this.minecraft.options.getBackgroundColor(0));
|
||||
- p_283501_.drawString(this.getFont(), mutablecomponent, j, k, 16777215 + (l << 24));
|
||||
+ p_283501_.drawString(font, highlightTip, j, k, 16777215 + (l << 24));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1146,7 +_,7 @@
|
||||
@@ -1155,7 +_,7 @@
|
||||
ItemStack itemstack = this.minecraft.player.getInventory().getSelected();
|
||||
if (itemstack.isEmpty()) {
|
||||
this.toolHighlightTimer = 0;
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
public static final float MAX_GUI_Z = 10000.0F;
|
||||
public static final float MIN_GUI_Z = -10000.0F;
|
||||
private static final int EXTRA_SPACE_AFTER_FIRST_TOOLTIP_LINE = 2;
|
||||
@@ -268,6 +_,11 @@
|
||||
@@ -280,6 +_,11 @@
|
||||
}
|
||||
|
||||
public int drawString(Font p_283343_, @Nullable String p_281896_, int p_283569_, int p_283418_, int p_281560_, boolean p_282130_) {
|
||||
@@ -21,7 +21,7 @@
|
||||
if (p_281896_ == null) {
|
||||
return 0;
|
||||
} else {
|
||||
@@ -294,6 +_,11 @@
|
||||
@@ -306,6 +_,11 @@
|
||||
}
|
||||
|
||||
public int drawString(Font p_282636_, FormattedCharSequence p_281596_, int p_281586_, int p_282816_, int p_281743_, boolean p_282394_) {
|
||||
@@ -33,7 +33,7 @@
|
||||
int i = p_282636_.drawInBatch(
|
||||
p_281596_,
|
||||
(float)p_281586_,
|
||||
@@ -864,6 +_,7 @@
|
||||
@@ -872,6 +_,7 @@
|
||||
CrashReport crashreport = CrashReport.forThrowable(throwable, "Rendering item");
|
||||
CrashReportCategory crashreportcategory = crashreport.addCategory("Item being rendered");
|
||||
crashreportcategory.setDetail("Item Type", () -> String.valueOf(p_281675_.getItem()));
|
||||
@@ -41,7 +41,7 @@
|
||||
crashreportcategory.setDetail("Item Components", () -> String.valueOf(p_281675_.getComponents()));
|
||||
crashreportcategory.setDetail("Item Foil", () -> String.valueOf(p_281675_.hasFoil()));
|
||||
throw new ReportedException(crashreport);
|
||||
@@ -904,16 +_,26 @@
|
||||
@@ -912,16 +_,26 @@
|
||||
}
|
||||
|
||||
this.pose.popPose();
|
||||
@@ -70,7 +70,7 @@
|
||||
this.renderTooltipInternal(p_283128_, list, p_283678_, p_281696_, DefaultTooltipPositioner.INSTANCE);
|
||||
}
|
||||
|
||||
@@ -922,7 +_,22 @@
|
||||
@@ -930,7 +_,22 @@
|
||||
}
|
||||
|
||||
public void renderComponentTooltip(Font p_282739_, List<Component> p_281832_, int p_282191_, int p_282446_) {
|
||||
@@ -94,7 +94,7 @@
|
||||
}
|
||||
|
||||
public void renderTooltip(Font p_282192_, List<? extends FormattedCharSequence> p_282297_, int p_281680_, int p_283325_) {
|
||||
@@ -941,11 +_,13 @@
|
||||
@@ -949,11 +_,13 @@
|
||||
|
||||
private void renderTooltipInternal(Font p_282675_, List<ClientTooltipComponent> p_282615_, int p_283230_, int p_283417_, ClientTooltipPositioner p_282442_) {
|
||||
if (!p_282615_.isEmpty()) {
|
||||
@@ -109,7 +109,7 @@
|
||||
if (k > i) {
|
||||
i = k;
|
||||
}
|
||||
@@ -955,18 +_,21 @@
|
||||
@@ -963,18 +_,21 @@
|
||||
|
||||
int i2 = i;
|
||||
int j2 = j;
|
||||
@@ -134,7 +134,7 @@
|
||||
k1 += clienttooltipcomponent1.getHeight() + (l1 == 0 ? 2 : 0);
|
||||
}
|
||||
|
||||
@@ -974,7 +_,7 @@
|
||||
@@ -982,7 +_,7 @@
|
||||
|
||||
for (int k2 = 0; k2 < p_282615_.size(); k2++) {
|
||||
ClientTooltipComponent clienttooltipcomponent2 = p_282615_.get(k2);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
--- a/net/minecraft/client/gui/components/AbstractButton.java
|
||||
+++ b/net/minecraft/client/gui/components/AbstractButton.java
|
||||
@@ -34,7 +_,7 @@
|
||||
@@ -32,7 +_,7 @@
|
||||
RenderSystem.enableDepthTest();
|
||||
p_281670_.blitSprite(SPRITES.get(this.active, this.isHoveredOrFocused()), this.getX(), this.getY(), this.getWidth(), this.getHeight());
|
||||
p_281670_.setColor(1.0F, 1.0F, 1.0F, 1.0F);
|
||||
|
||||
@@ -79,13 +79,13 @@
|
||||
+ protected void drawGameInformation(GuiGraphics p_281525_) {
|
||||
+ List<String> list = this.getGameInformation();
|
||||
+ list.addAll(this.getOverlayHelp());
|
||||
+ net.minecraftforge.client.ForgeHooksClient.onCustomizeDebugEvent(p_281525_, minecraft.getWindow(), minecraft.getTimer().getRealtimeDeltaTicks(), list, true);
|
||||
+ net.minecraftforge.client.ForgeHooksClient.onCustomizeDebugEvent(p_281525_, minecraft.getWindow(), minecraft.getFrameTime(), list, true);
|
||||
+ this.renderLines(p_281525_, list, true);
|
||||
}
|
||||
|
||||
protected void drawSystemInformation(GuiGraphics p_281261_) {
|
||||
List<String> list = this.getSystemInformation();
|
||||
+ net.minecraftforge.client.ForgeHooksClient.onCustomizeDebugEvent(p_281261_, minecraft.getWindow(), minecraft.getTimer().getRealtimeDeltaTicks(), list, false);
|
||||
+ net.minecraftforge.client.ForgeHooksClient.onCustomizeDebugEvent(p_281261_, minecraft.getWindow(), minecraft.getFrameTime(), list, false);
|
||||
this.renderLines(p_281261_, list, false);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
--- a/net/minecraft/client/gui/screens/PauseScreen.java
|
||||
+++ b/net/minecraft/client/gui/screens/PauseScreen.java
|
||||
@@ -94,6 +_,7 @@
|
||||
@@ -86,6 +_,7 @@
|
||||
} else {
|
||||
gridlayout$rowhelper.addChild(this.openScreenButton(PLAYER_REPORTING, () -> new SocialInteractionsScreen(this)));
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
--- a/net/minecraft/client/gui/screens/Screen.java
|
||||
+++ b/net/minecraft/client/gui/screens/Screen.java
|
||||
@@ -201,7 +_,7 @@
|
||||
@@ -204,7 +_,7 @@
|
||||
}
|
||||
|
||||
public void onClose() {
|
||||
@@ -9,7 +9,7 @@
|
||||
}
|
||||
|
||||
protected <T extends GuiEventListener & Renderable & NarratableEntry> T addRenderableWidget(T p_169406_) {
|
||||
@@ -312,8 +_,10 @@
|
||||
@@ -320,8 +_,10 @@
|
||||
this.width = p_96608_;
|
||||
this.height = p_96609_;
|
||||
if (!this.initialized) {
|
||||
@@ -20,7 +20,7 @@
|
||||
} else {
|
||||
this.repositionElements();
|
||||
}
|
||||
@@ -326,8 +_,10 @@
|
||||
@@ -334,8 +_,10 @@
|
||||
protected void rebuildWidgets() {
|
||||
this.clearWidgets();
|
||||
this.clearFocus();
|
||||
@@ -31,7 +31,7 @@
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -354,6 +_,7 @@
|
||||
@@ -362,6 +_,7 @@
|
||||
|
||||
this.renderBlurredBackground(p_297268_);
|
||||
this.renderMenuBackground(p_283688_);
|
||||
@@ -39,7 +39,7 @@
|
||||
}
|
||||
|
||||
protected void renderBlurredBackground(float p_336041_) {
|
||||
@@ -463,6 +_,19 @@
|
||||
@@ -492,6 +_,19 @@
|
||||
}
|
||||
|
||||
public void onFilesDrop(List<Path> p_96591_) {
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
--- a/net/minecraft/client/gui/screens/TitleScreen.java
|
||||
+++ b/net/minecraft/client/gui/screens/TitleScreen.java
|
||||
@@ -60,6 +_,7 @@
|
||||
private boolean fading;
|
||||
private long fadeInStart;
|
||||
@@ -61,6 +_,7 @@
|
||||
@Nullable
|
||||
private TitleScreen.WarningLabel warningLabel;
|
||||
private final LogoRenderer logoRenderer;
|
||||
+ private net.minecraftforge.client.gui.TitleScreenModUpdateIndicator modUpdateNotification;
|
||||
|
||||
public TitleScreen() {
|
||||
this(false);
|
||||
@@ -115,11 +_,15 @@
|
||||
@@ -116,11 +_,15 @@
|
||||
int j = this.width - i - 2;
|
||||
int k = 24;
|
||||
int l = this.height / 4 + 48;
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
SpriteIconButton spriteiconbutton = this.addRenderableWidget(
|
||||
CommonButtons.language(
|
||||
@@ -170,7 +_,7 @@
|
||||
@@ -171,7 +_,7 @@
|
||||
}).bounds(this.width / 2 - 100, p_96764_ + p_96765_ * 1, 200, 20).tooltip(tooltip).build()).active = flag;
|
||||
this.addRenderableWidget(
|
||||
Button.builder(Component.translatable("menu.online"), p_325369_ -> this.minecraft.setScreen(new RealmsMainScreen(this)))
|
||||
@@ -33,24 +33,24 @@
|
||||
.tooltip(tooltip)
|
||||
.build()
|
||||
)
|
||||
@@ -276,6 +_,7 @@
|
||||
if ((i & -67108864) != 0) {
|
||||
super.render(p_282860_, p_281753_, p_283539_, p_282628_);
|
||||
this.logoRenderer.renderLogo(p_282860_, this.width, f);
|
||||
@@ -281,6 +_,7 @@
|
||||
this.warningLabel.render(p_282860_, i);
|
||||
}
|
||||
|
||||
+ net.minecraftforge.client.ForgeHooksClient.renderMainMenu(this, p_282860_, this.font, this.width, this.height, i);
|
||||
if (this.splash != null && !this.minecraft.options.hideSplashTexts().get()) {
|
||||
this.splash.render(p_282860_, this.width, this.font, i);
|
||||
}
|
||||
@@ -291,11 +_,19 @@
|
||||
@@ -296,11 +_,19 @@
|
||||
s = s + I18n.get("menu.modded");
|
||||
}
|
||||
|
||||
- p_282860_.drawString(this.font, s, 2, this.height - 10, 16777215 | i);
|
||||
+ net.minecraftforge.internal.BrandingControl.forEachLine(true, true, (brd, brdline) ->
|
||||
+ net.minecraftforge.internal.BrandingControl.forEachLine(true, true, (brdline, brd) ->
|
||||
+ p_282860_.drawString(this.font, brd, 2, this.height - ( 10 + brdline * (this.font.lineHeight + 1)), 16777215 | i)
|
||||
+ );
|
||||
+
|
||||
+ net.minecraftforge.internal.BrandingControl.forEachAboveCopyrightLine((brd, brdline) ->
|
||||
+ net.minecraftforge.internal.BrandingControl.forEachAboveCopyrightLine((brdline, brd) ->
|
||||
+ p_282860_.drawString(this.font, brd, this.width - font.width(brd), this.height - (10 + (brdline + 1) * ( this.font.lineHeight + 1)), 16777215 | i)
|
||||
+ );
|
||||
+
|
||||
|
||||
+11
-11
@@ -1,5 +1,5 @@
|
||||
--- a/net/minecraft/client/gui/screens/options/controls/KeyBindsList.java
|
||||
+++ b/net/minecraft/client/gui/screens/options/controls/KeyBindsList.java
|
||||
--- a/net/minecraft/client/gui/screens/controls/KeyBindsList.java
|
||||
+++ b/net/minecraft/client/gui/screens/controls/KeyBindsList.java
|
||||
@@ -69,6 +_,11 @@
|
||||
return 340;
|
||||
}
|
||||
@@ -13,25 +13,25 @@
|
||||
public class CategoryEntry extends KeyBindsList.Entry {
|
||||
final Component name;
|
||||
@@ -153,9 +_,10 @@
|
||||
this.name = p_343976_;
|
||||
this.changeButton = Button.builder(p_343976_, p_342196_ -> {
|
||||
KeyBindsList.this.keyBindsScreen.selectedKey = p_343088_;
|
||||
+ p_343088_.setKeyModifierAndCode(null, com.mojang.blaze3d.platform.InputConstants.UNKNOWN);
|
||||
this.name = p_193917_;
|
||||
this.changeButton = Button.builder(p_193917_, p_269618_ -> {
|
||||
KeyBindsList.this.keyBindsScreen.selectedKey = p_193916_;
|
||||
+ p_193916_.setKeyModifierAndCode(null, com.mojang.blaze3d.platform.InputConstants.UNKNOWN);
|
||||
KeyBindsList.this.resetMappingAndUpdateButtons();
|
||||
})
|
||||
- .bounds(0, 0, 75, 20)
|
||||
+ .bounds(0, 0, 75 + 20 /* Forge: Add space */, 20)
|
||||
.createNarration(
|
||||
p_342179_ -> p_343088_.isUnbound()
|
||||
? Component.translatable("narrator.controls.unbound", p_343976_)
|
||||
p_253311_ -> p_193916_.isUnbound()
|
||||
? Component.translatable("narrator.controls.unbound", p_193917_)
|
||||
@@ -163,6 +_,7 @@
|
||||
)
|
||||
.build();
|
||||
this.resetButton = Button.builder(RESET_BUTTON_TITLE, p_343650_ -> {
|
||||
this.resetButton = Button.builder(RESET_BUTTON_TITLE, p_325376_ -> {
|
||||
+ this.key.setToDefault();
|
||||
KeyBindsList.this.minecraft.options.setKey(p_343088_, p_343088_.getDefaultKey());
|
||||
KeyBindsList.this.minecraft.options.setKey(p_193916_, p_193916_.getDefaultKey());
|
||||
KeyBindsList.this.resetMappingAndUpdateButtons();
|
||||
}).bounds(0, 0, 50, 20).createNarration(p_344192_ -> Component.translatable("narrator.controls.reset", p_343976_)).build();
|
||||
}).bounds(0, 0, 50, 20).createNarration(p_253313_ -> Component.translatable("narrator.controls.reset", p_193917_)).build();
|
||||
@@ -215,7 +_,7 @@
|
||||
MutableComponent mutablecomponent = Component.empty();
|
||||
if (!this.key.isUnbound()) {
|
||||
@@ -0,0 +1,46 @@
|
||||
--- a/net/minecraft/client/gui/screens/controls/KeyBindsScreen.java
|
||||
+++ b/net/minecraft/client/gui/screens/controls/KeyBindsScreen.java
|
||||
@@ -33,7 +_,7 @@
|
||||
this.keyBindsList = this.addRenderableWidget(new KeyBindsList(this, this.minecraft));
|
||||
this.resetButton = Button.builder(Component.translatable("controls.resetAll"), p_269619_ -> {
|
||||
for (KeyMapping keymapping : this.options.keyMappings) {
|
||||
- keymapping.setKey(keymapping.getDefaultKey());
|
||||
+ keymapping.setToDefault();
|
||||
}
|
||||
|
||||
this.keyBindsList.resetMappingAndUpdateButtons();
|
||||
@@ -70,11 +_,14 @@
|
||||
public boolean keyPressed(int p_193987_, int p_193988_, int p_193989_) {
|
||||
if (this.selectedKey != null) {
|
||||
if (p_193987_ == 256) {
|
||||
+ this.selectedKey.setKeyModifierAndCode(null, InputConstants.UNKNOWN);
|
||||
this.options.setKey(this.selectedKey, InputConstants.UNKNOWN);
|
||||
} else {
|
||||
+ this.selectedKey.setKeyModifierAndCode(null, InputConstants.getKey(p_193987_, p_193988_));
|
||||
this.options.setKey(this.selectedKey, InputConstants.getKey(p_193987_, p_193988_));
|
||||
}
|
||||
|
||||
+ if (p_193987_ == 256 || !net.minecraftforge.client.settings.KeyModifier.isKeyCodeModifier(this.selectedKey.getKey()))
|
||||
this.selectedKey = null;
|
||||
this.lastKeySelection = Util.getMillis();
|
||||
this.keyBindsList.resetMappingAndUpdateButtons();
|
||||
@@ -82,6 +_,19 @@
|
||||
} else {
|
||||
return super.keyPressed(p_193987_, p_193988_, p_193989_);
|
||||
}
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public boolean keyReleased(int keyCode, int scanCode, int modifiers) {
|
||||
+ // Forge: We wait for a second key above if the first press is a modifier
|
||||
+ // but if they release the modifier then set it explicitly.
|
||||
+ var key = InputConstants.getKey(keyCode, scanCode);
|
||||
+ if (this.selectedKey != null && this.selectedKey.getKey() == key) {
|
||||
+ this.selectedKey = null;
|
||||
+ this.lastKeySelection = Util.getMillis();
|
||||
+ this.keyBindsList.resetMappingAndUpdateButtons();
|
||||
+ }
|
||||
+ return super.keyReleased(keyCode, scanCode, modifiers);
|
||||
}
|
||||
|
||||
@Override
|
||||
+45
-37
@@ -7,9 +7,9 @@
|
||||
+ private final List<net.minecraftforge.client.gui.CreativeTabsScreenPage> pages = new java.util.ArrayList<>();
|
||||
+ private net.minecraftforge.client.gui.CreativeTabsScreenPage currentPage = new net.minecraftforge.client.gui.CreativeTabsScreenPage(new java.util.ArrayList<>());
|
||||
|
||||
public CreativeModeInventoryScreen(LocalPlayer p_344408_, FeatureFlagSet p_260074_, boolean p_259569_) {
|
||||
super(new CreativeModeInventoryScreen.ItemPickerMenu(p_344408_), p_344408_.getInventory(), CommonComponents.EMPTY);
|
||||
@@ -159,7 +_,7 @@
|
||||
public CreativeModeInventoryScreen(Player p_259788_, FeatureFlagSet p_260074_, boolean p_259569_) {
|
||||
super(new CreativeModeInventoryScreen.ItemPickerMenu(p_259788_), p_259788_.getInventory(), CommonComponents.EMPTY);
|
||||
@@ -144,7 +_,7 @@
|
||||
private void refreshCurrentTabContents(Collection<ItemStack> p_261591_) {
|
||||
int i = this.menu.getRowIndexForScroll(this.scrollOffs);
|
||||
this.menu.items.clear();
|
||||
@@ -18,7 +18,7 @@
|
||||
this.refreshSearchResults();
|
||||
} else {
|
||||
this.menu.items.addAll(p_261591_);
|
||||
@@ -322,6 +_,35 @@
|
||||
@@ -307,6 +_,35 @@
|
||||
protected void init() {
|
||||
if (this.minecraft.gameMode.hasInfiniteItems()) {
|
||||
super.init();
|
||||
@@ -54,7 +54,7 @@
|
||||
this.searchBox = new EditBox(this.font, this.leftPos + 82, this.topPos + 6, 80, 9, Component.translatable("itemGroup.search"));
|
||||
this.searchBox.setMaxLength(50);
|
||||
this.searchBox.setBordered(false);
|
||||
@@ -368,7 +_,7 @@
|
||||
@@ -353,7 +_,7 @@
|
||||
public boolean charTyped(char p_98521_, int p_98522_) {
|
||||
if (this.ignoreTextInput) {
|
||||
return false;
|
||||
@@ -63,7 +63,7 @@
|
||||
return false;
|
||||
} else {
|
||||
String s = this.searchBox.getValue();
|
||||
@@ -387,7 +_,7 @@
|
||||
@@ -372,7 +_,7 @@
|
||||
@Override
|
||||
public boolean keyPressed(int p_98547_, int p_98548_, int p_98549_) {
|
||||
this.ignoreTextInput = false;
|
||||
@@ -72,7 +72,7 @@
|
||||
if (this.minecraft.options.keyChat.matches(p_98547_, p_98548_)) {
|
||||
this.ignoreTextInput = true;
|
||||
this.selectTab(CreativeModeTabs.searchTab());
|
||||
@@ -423,6 +_,7 @@
|
||||
@@ -408,6 +_,7 @@
|
||||
}
|
||||
|
||||
private void refreshSearchResults() {
|
||||
@@ -80,20 +80,20 @@
|
||||
this.menu.items.clear();
|
||||
this.visibleTags.clear();
|
||||
String s = this.searchBox.getValue();
|
||||
@@ -435,10 +_,10 @@
|
||||
SearchTree<ItemStack> searchtree;
|
||||
if (s.startsWith("#")) {
|
||||
s = s.substring(1);
|
||||
- searchtree = sessionsearchtrees.creativeTagSearch();
|
||||
+ searchtree = sessionsearchtrees.getSearchTree(net.minecraftforge.client.CreativeModeTabSearchRegistry.getTagSearchKey(selectedTab));
|
||||
this.updateVisibleTags(s);
|
||||
} else {
|
||||
- searchtree = sessionsearchtrees.creativeNameSearch();
|
||||
+ searchtree = sessionsearchtrees.getSearchTree(net.minecraftforge.client.CreativeModeTabSearchRegistry.getNameSearchKey(selectedTab));
|
||||
}
|
||||
@@ -417,10 +_,10 @@
|
||||
SearchTree<ItemStack> searchtree;
|
||||
if (s.startsWith("#")) {
|
||||
s = s.substring(1);
|
||||
- searchtree = this.minecraft.getSearchTree(SearchRegistry.CREATIVE_TAGS);
|
||||
+ searchtree = this.minecraft.getSearchTree(net.minecraftforge.client.CreativeModeTabSearchRegistry.getTagSearchKey(selectedTab));
|
||||
this.updateVisibleTags(s);
|
||||
} else {
|
||||
- searchtree = this.minecraft.getSearchTree(SearchRegistry.CREATIVE_NAMES);
|
||||
+ searchtree = this.minecraft.getSearchTree(net.minecraftforge.client.CreativeModeTabSearchRegistry.getNameSearchKey(selectedTab));
|
||||
}
|
||||
|
||||
this.menu.items.addAll(searchtree.search(s.toLowerCase(Locale.ROOT)));
|
||||
@@ -466,7 +_,8 @@
|
||||
this.menu.items.addAll(searchtree.search(s.toLowerCase(Locale.ROOT)));
|
||||
@@ -447,7 +_,8 @@
|
||||
@Override
|
||||
protected void renderLabels(GuiGraphics p_283168_, int p_281774_, int p_281466_) {
|
||||
if (selectedTab.showTitle()) {
|
||||
@@ -103,7 +103,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
@@ -476,7 +_,7 @@
|
||||
@@ -457,7 +_,7 @@
|
||||
double d0 = p_98531_ - (double)this.leftPos;
|
||||
double d1 = p_98532_ - (double)this.topPos;
|
||||
|
||||
@@ -112,7 +112,7 @@
|
||||
if (this.checkTabClicked(creativemodetab, d0, d1)) {
|
||||
return true;
|
||||
}
|
||||
@@ -498,7 +_,7 @@
|
||||
@@ -479,7 +_,7 @@
|
||||
double d1 = p_98623_ - (double)this.topPos;
|
||||
this.scrolling = false;
|
||||
|
||||
@@ -121,7 +121,7 @@
|
||||
if (this.checkTabClicked(creativemodetab, d0, d1)) {
|
||||
this.selectTab(creativemodetab);
|
||||
return true;
|
||||
@@ -516,6 +_,7 @@
|
||||
@@ -497,6 +_,7 @@
|
||||
private void selectTab(CreativeModeTab p_98561_) {
|
||||
CreativeModeTab creativemodetab = selectedTab;
|
||||
selectedTab = p_98561_;
|
||||
@@ -129,7 +129,7 @@
|
||||
this.quickCraftSlots.clear();
|
||||
this.menu.items.clear();
|
||||
this.clearDraggingState();
|
||||
@@ -592,13 +_,15 @@
|
||||
@@ -573,13 +_,15 @@
|
||||
this.originalSlots = null;
|
||||
}
|
||||
|
||||
@@ -146,7 +146,7 @@
|
||||
|
||||
this.refreshSearchResults();
|
||||
} else {
|
||||
@@ -661,7 +_,15 @@
|
||||
@@ -642,7 +_,15 @@
|
||||
public void render(GuiGraphics p_283000_, int p_281317_, int p_282770_, float p_281295_) {
|
||||
super.render(p_283000_, p_281317_, p_282770_, p_281295_);
|
||||
|
||||
@@ -163,7 +163,7 @@
|
||||
if (this.checkTabHovering(p_283000_, creativemodetab, p_281317_, p_282770_)) {
|
||||
break;
|
||||
}
|
||||
@@ -673,6 +_,7 @@
|
||||
@@ -654,6 +_,7 @@
|
||||
p_283000_.renderTooltip(this.font, TRASH_SLOT_TOOLTIP, p_281317_, p_282770_);
|
||||
}
|
||||
|
||||
@@ -171,16 +171,16 @@
|
||||
this.renderTooltip(p_283000_, p_281317_, p_282770_);
|
||||
}
|
||||
|
||||
@@ -680,7 +_,7 @@
|
||||
@@ -661,7 +_,7 @@
|
||||
public List<Component> getTooltipFromContainerItem(ItemStack p_281769_) {
|
||||
boolean flag = this.hoveredSlot != null && this.hoveredSlot instanceof CreativeModeInventoryScreen.CustomCreativeSlot;
|
||||
boolean flag1 = selectedTab.getType() == CreativeModeTab.Type.CATEGORY;
|
||||
- boolean flag2 = selectedTab.getType() == CreativeModeTab.Type.SEARCH;
|
||||
+ boolean flag2 = selectedTab.hasSearchBar();
|
||||
+ boolean flag2 =selectedTab.hasSearchBar();
|
||||
TooltipFlag.Default tooltipflag$default = this.minecraft.options.advancedItemTooltips ? TooltipFlag.Default.ADVANCED : TooltipFlag.Default.NORMAL;
|
||||
TooltipFlag tooltipflag = flag ? tooltipflag$default.asCreative() : tooltipflag$default;
|
||||
List<Component> list = p_281769_.getTooltipLines(Item.TooltipContext.of(this.minecraft.level), this.minecraft.player, tooltipflag);
|
||||
@@ -699,7 +_,7 @@
|
||||
@@ -680,7 +_,7 @@
|
||||
int i = 1;
|
||||
|
||||
for (CreativeModeTab creativemodetab : CreativeModeTabs.tabs()) {
|
||||
@@ -189,7 +189,7 @@
|
||||
list1.add(i++, creativemodetab.getDisplayName().copy().withStyle(ChatFormatting.BLUE));
|
||||
}
|
||||
}
|
||||
@@ -710,7 +_,7 @@
|
||||
@@ -691,14 +_,14 @@
|
||||
|
||||
@Override
|
||||
protected void renderBg(GuiGraphics p_282663_, float p_282504_, int p_282089_, int p_282249_) {
|
||||
@@ -198,7 +198,15 @@
|
||||
if (creativemodetab != selectedTab) {
|
||||
this.renderTabButton(p_282663_, creativemodetab);
|
||||
}
|
||||
@@ -726,6 +_,7 @@
|
||||
}
|
||||
|
||||
p_282663_.blit(
|
||||
- new ResourceLocation("textures/gui/container/creative_inventory/tab_" + selectedTab.getBackgroundSuffix()),
|
||||
+ selectedTab.getBackgroundLocation(),
|
||||
this.leftPos,
|
||||
this.topPos,
|
||||
0,
|
||||
@@ -715,6 +_,7 @@
|
||||
p_282663_.blitSprite(resourcelocation, j, k + (int)((float)(i - k - 17) * this.scrollOffs), 12, 15);
|
||||
}
|
||||
|
||||
@@ -206,7 +214,7 @@
|
||||
this.renderTabButton(p_282663_, selectedTab);
|
||||
if (selectedTab.getType() == CreativeModeTab.Type.INVENTORY) {
|
||||
InventoryScreen.renderEntityInInventoryFollowsMouse(
|
||||
@@ -744,7 +_,7 @@
|
||||
@@ -733,7 +_,7 @@
|
||||
}
|
||||
|
||||
private int getTabX(CreativeModeTab p_260136_) {
|
||||
@@ -215,7 +223,7 @@
|
||||
int j = 27;
|
||||
int k = 27 * i;
|
||||
if (p_260136_.isAlignedRight()) {
|
||||
@@ -756,7 +_,7 @@
|
||||
@@ -745,7 +_,7 @@
|
||||
|
||||
private int getTabY(CreativeModeTab p_260181_) {
|
||||
int i = 0;
|
||||
@@ -224,7 +232,7 @@
|
||||
i -= 32;
|
||||
} else {
|
||||
i += this.imageHeight;
|
||||
@@ -784,8 +_,8 @@
|
||||
@@ -773,8 +_,8 @@
|
||||
|
||||
protected void renderTabButton(GuiGraphics p_283590_, CreativeModeTab p_283489_) {
|
||||
boolean flag = p_283489_ == selectedTab;
|
||||
@@ -235,7 +243,7 @@
|
||||
int j = this.leftPos + this.getTabX(p_283489_);
|
||||
int k = this.topPos - (flag1 ? 28 : -(this.imageHeight - 4));
|
||||
ResourceLocation[] aresourcelocation;
|
||||
@@ -795,6 +_,7 @@
|
||||
@@ -784,6 +_,7 @@
|
||||
aresourcelocation = flag ? SELECTED_BOTTOM_TABS : UNSELECTED_BOTTOM_TABS;
|
||||
}
|
||||
|
||||
@@ -243,7 +251,7 @@
|
||||
p_283590_.blitSprite(aresourcelocation[Mth.clamp(i, 0, aresourcelocation.length)], j, k, 26, 32);
|
||||
p_283590_.pose().pushPose();
|
||||
p_283590_.pose().translate(0.0F, 0.0F, 100.0F);
|
||||
@@ -836,6 +_,14 @@
|
||||
@@ -825,6 +_,14 @@
|
||||
}
|
||||
}
|
||||
|
||||
@@ -258,7 +266,7 @@
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
static class CustomCreativeSlot extends Slot {
|
||||
public CustomCreativeSlot(Container p_98633_, int p_98634_, int p_98635_, int p_98636_) {
|
||||
@@ -1020,6 +_,22 @@
|
||||
@@ -1009,6 +_,22 @@
|
||||
@Override
|
||||
public boolean mayPickup(Player p_98665_) {
|
||||
return this.target.mayPickup(p_98665_);
|
||||
|
||||
+18
-22
@@ -1,6 +1,6 @@
|
||||
--- a/net/minecraft/client/gui/screens/inventory/EnchantmentScreen.java
|
||||
+++ b/net/minecraft/client/gui/screens/inventory/EnchantmentScreen.java
|
||||
@@ -111,7 +_,7 @@
|
||||
@@ -108,7 +_,7 @@
|
||||
int l1 = 86 - this.font.width(s);
|
||||
FormattedText formattedtext = EnchantmentNames.getInstance().getRandomName(this.font, l1);
|
||||
int i2 = 6839882;
|
||||
@@ -9,24 +9,20 @@
|
||||
RenderSystem.enableBlend();
|
||||
p_282430_.blitSprite(ENCHANTMENT_SLOT_DISABLED_SPRITE, i1, j + 14 + 19 * l, 108, 19);
|
||||
p_282430_.blitSprite(DISABLED_LEVEL_SPRITES[l], i1 + 1, j + 15 + 19 * l, 16, 16);
|
||||
@@ -177,13 +_,16 @@
|
||||
.registryAccess()
|
||||
.registryOrThrow(Registries.ENCHANTMENT)
|
||||
.getHolder(this.menu.enchantClue[j]);
|
||||
- if (!optional.isEmpty()) {
|
||||
+ {
|
||||
int l = this.menu.levelClue[j];
|
||||
int i1 = j + 1;
|
||||
- if (this.isHovering(60, 14 + 19 * j, 108, 17, (double)p_282491_, (double)p_281953_) && k > 0 && l >= 0 && optional != null) {
|
||||
+ if (this.isHovering(60, 14 + 19 * j, 108, 17, (double)p_282491_, (double)p_281953_) && k > 0) {
|
||||
List<Component> list = Lists.newArrayList();
|
||||
- list.add(Component.translatable("container.enchant.clue", Enchantment.getFullname(optional.get(), l)).withStyle(ChatFormatting.WHITE));
|
||||
- if (!flag) {
|
||||
+ list.add(Component.translatable("container.enchant.clue", optional.isEmpty() ? "" : Enchantment.getFullname(optional.get(), l)).withStyle(ChatFormatting.WHITE));
|
||||
+ if (optional.isEmpty()) {
|
||||
+ list.add(Component.literal(""));
|
||||
+ list.add(Component.translatable("forge.container.enchant.limitedEnchantability").withStyle(ChatFormatting.RED));
|
||||
+ } else if (!flag) {
|
||||
list.add(CommonComponents.EMPTY);
|
||||
if (this.minecraft.player.experienceLevel < k) {
|
||||
list.add(Component.translatable("container.enchant.level.requirement", this.menu.costs[j]).withStyle(ChatFormatting.RED));
|
||||
@@ -173,10 +_,13 @@
|
||||
Enchantment enchantment = Enchantment.byId(this.menu.enchantClue[j]);
|
||||
int l = this.menu.levelClue[j];
|
||||
int i1 = j + 1;
|
||||
- if (this.isHovering(60, 14 + 19 * j, 108, 17, (double)p_282491_, (double)p_281953_) && k > 0 && l >= 0 && enchantment != null) {
|
||||
+ if (this.isHovering(60, 14 + 19 * j, 108, 17, (double)p_282491_, (double)p_281953_) && k > 0) {
|
||||
List<Component> list = Lists.newArrayList();
|
||||
- list.add(Component.translatable("container.enchant.clue", enchantment.getFullname(l)).withStyle(ChatFormatting.WHITE));
|
||||
- if (!flag) {
|
||||
+ list.add((Component.translatable("container.enchant.clue", enchantment == null ? "" : enchantment.getFullname(l))).withStyle(ChatFormatting.WHITE));
|
||||
+ if (enchantment == null) {
|
||||
+ list.add(Component.literal(""));
|
||||
+ list.add(Component.translatable("forge.container.enchant.limitedEnchantability").withStyle(ChatFormatting.RED));
|
||||
+ } else if (!flag) {
|
||||
list.add(CommonComponents.EMPTY);
|
||||
if (this.minecraft.player.experienceLevel < k) {
|
||||
list.add(Component.translatable("container.enchant.level.requirement", this.menu.costs[j]).withStyle(ChatFormatting.RED));
|
||||
+2
-2
@@ -4,8 +4,8 @@
|
||||
private static final Vector3f TEXT_SCALE = new Vector3f(1.0F, 1.0F, 1.0F);
|
||||
private static final int TEXTURE_WIDTH = 16;
|
||||
private static final int TEXTURE_HEIGHT = 16;
|
||||
- private final ResourceLocation texture = ResourceLocation.withDefaultNamespace("textures/gui/hanging_signs/" + this.woodType.name() + ".png");
|
||||
+ private final ResourceLocation texture = ResourceLocation.parse(this.woodType.name() + ".png").withPrefix("textures/gui/hanging_signs/");
|
||||
- private final ResourceLocation texture = new ResourceLocation("textures/gui/hanging_signs/" + this.woodType.name() + ".png");
|
||||
+ private final ResourceLocation texture = new ResourceLocation(this.woodType.name() + ".png").withPrefix("textures/gui/hanging_signs/"); // possibly unnecessary patch(?) -Paint_Ninja
|
||||
|
||||
public HangingSignEditScreen(SignBlockEntity p_278017_, boolean p_277942_, boolean p_277778_) {
|
||||
super(p_278017_, p_277942_, p_277778_, Component.translatable("hanging_sign.edit"));
|
||||
+3
-3
@@ -1,8 +1,8 @@
|
||||
--- a/net/minecraft/client/gui/screens/inventory/tooltip/ClientTooltipComponent.java
|
||||
+++ b/net/minecraft/client/gui/screens/inventory/tooltip/ClientTooltipComponent.java
|
||||
@@ -22,6 +_,8 @@
|
||||
} else if (p_169951_ instanceof ClientActivePlayersTooltip.ActivePlayersTooltip clientactiveplayerstooltip$activeplayerstooltip) {
|
||||
return new ClientActivePlayersTooltip(clientactiveplayerstooltip$activeplayerstooltip);
|
||||
@@ -20,6 +_,8 @@
|
||||
if (p_169951_ instanceof BundleTooltip bundletooltip) {
|
||||
return new ClientBundleTooltip(bundletooltip.contents());
|
||||
} else {
|
||||
+ ClientTooltipComponent result = net.minecraftforge.client.gui.ClientTooltipComponentManager.createClientTooltipComponent(p_169951_);
|
||||
+ if (result != null) return result;
|
||||
|
||||
-45
@@ -1,45 +0,0 @@
|
||||
--- a/net/minecraft/client/gui/screens/options/controls/KeyBindsScreen.java
|
||||
+++ b/net/minecraft/client/gui/screens/options/controls/KeyBindsScreen.java
|
||||
@@ -41,7 +_,7 @@
|
||||
protected void addFooter() {
|
||||
this.resetButton = Button.builder(Component.translatable("controls.resetAll"), p_343640_ -> {
|
||||
for (KeyMapping keymapping : this.options.keyMappings) {
|
||||
- keymapping.setKey(keymapping.getDefaultKey());
|
||||
+ keymapping.setToDefault();
|
||||
}
|
||||
|
||||
this.keyBindsList.resetMappingAndUpdateButtons();
|
||||
@@ -73,11 +_,14 @@
|
||||
public boolean keyPressed(int p_342715_, int p_342862_, int p_345515_) {
|
||||
if (this.selectedKey != null) {
|
||||
if (p_342715_ == 256) {
|
||||
+ this.selectedKey.setKeyModifierAndCode(null, InputConstants.UNKNOWN);
|
||||
this.options.setKey(this.selectedKey, InputConstants.UNKNOWN);
|
||||
} else {
|
||||
+ this.selectedKey.setKeyModifierAndCode(null, InputConstants.getKey(p_342715_, p_342862_));
|
||||
this.options.setKey(this.selectedKey, InputConstants.getKey(p_342715_, p_342862_));
|
||||
}
|
||||
|
||||
+ if (p_342715_ == 256 || !net.minecraftforge.client.settings.KeyModifier.isKeyCodeModifier(this.selectedKey.getKey()))
|
||||
this.selectedKey = null;
|
||||
this.lastKeySelection = Util.getMillis();
|
||||
this.keyBindsList.resetMappingAndUpdateButtons();
|
||||
@@ -100,5 +_,18 @@
|
||||
}
|
||||
|
||||
this.resetButton.active = flag;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public boolean keyReleased(int keyCode, int scanCode, int modifiers) {
|
||||
+ // Forge: We wait for a second key above if the first press is a modifier
|
||||
+ // but if they release the modifier then set it explicitly.
|
||||
+ var key = InputConstants.getKey(keyCode, scanCode);
|
||||
+ if (this.selectedKey != null && this.selectedKey.getKey() == key) {
|
||||
+ this.selectedKey = null;
|
||||
+ this.lastKeySelection = Util.getMillis();
|
||||
+ this.keyBindsList.resetMappingAndUpdateButtons();
|
||||
+ }
|
||||
+ return super.keyReleased(keyCode, scanCode, modifiers);
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -6,6 +6,6 @@
|
||||
p_100014_.setSelected(null);
|
||||
- p_100015_.forEach(
|
||||
+ p_100015_.filter(PackSelectionModel.Entry::notHidden).forEach(
|
||||
p_340813_ -> {
|
||||
p_325390_ -> {
|
||||
TransferableSelectionList.PackEntry transferableselectionlist$packentry1 = new TransferableSelectionList.PackEntry(
|
||||
this.minecraft, p_100014_, p_340813_
|
||||
this.minecraft, p_100014_, p_325390_
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
--- a/net/minecraft/client/gui/screens/recipebook/RecipeBookComponent.java
|
||||
+++ b/net/minecraft/client/gui/screens/recipebook/RecipeBookComponent.java
|
||||
@@ -115,7 +_,7 @@
|
||||
@@ -116,7 +_,7 @@
|
||||
this.initFilterButtonTextures();
|
||||
this.tabButtons.clear();
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
this.tabButtons.add(new RecipeBookTabButton(recipebookcategories));
|
||||
}
|
||||
|
||||
@@ -293,7 +_,7 @@
|
||||
@@ -291,7 +_,7 @@
|
||||
}
|
||||
|
||||
if (itemstack != null && this.minecraft.screen != null) {
|
||||
|
||||
+3
-3
@@ -1,6 +1,6 @@
|
||||
--- a/net/minecraft/client/gui/screens/worldselection/CreateWorldScreen.java
|
||||
+++ b/net/minecraft/client/gui/screens/worldselection/CreateWorldScreen.java
|
||||
@@ -118,6 +_,7 @@
|
||||
@@ -119,6 +_,7 @@
|
||||
public static void openFresh(Minecraft p_232897_, @Nullable Screen p_232898_) {
|
||||
queueLoadScreen(p_232897_, PREPARING_WORLD_DATA);
|
||||
PackRepository packrepository = new PackRepository(new ServerPacksSource(p_232897_.directoryValidator()));
|
||||
@@ -8,7 +8,7 @@
|
||||
WorldLoader.InitConfig worldloader$initconfig = createDefaultLoadConfig(packrepository, WorldDataConfiguration.DEFAULT);
|
||||
CompletableFuture<WorldCreationContext> completablefuture = WorldLoader.load(
|
||||
worldloader$initconfig,
|
||||
@@ -415,7 +_,7 @@
|
||||
@@ -419,7 +_,7 @@
|
||||
if (p_269627_) {
|
||||
p_270552_.accept(this.uiState.getSettings().dataConfiguration());
|
||||
} else {
|
||||
@@ -17,7 +17,7 @@
|
||||
}
|
||||
},
|
||||
Component.translatable("dataPack.validation.failed"),
|
||||
@@ -533,6 +_,7 @@
|
||||
@@ -537,6 +_,7 @@
|
||||
if (path != null) {
|
||||
if (this.tempDataPackRepository == null) {
|
||||
this.tempDataPackRepository = ServerPacksSource.createPackRepository(path, this.packValidator);
|
||||
|
||||
+3
-3
@@ -1,10 +1,10 @@
|
||||
--- a/net/minecraft/client/gui/screens/worldselection/WorldSelectionList.java
|
||||
+++ b/net/minecraft/client/gui/screens/worldselection/WorldSelectionList.java
|
||||
@@ -75,6 +_,7 @@
|
||||
static final ResourceLocation JOIN_HIGHLIGHTED_SPRITE = ResourceLocation.withDefaultNamespace("world_list/join_highlighted");
|
||||
static final ResourceLocation JOIN_SPRITE = ResourceLocation.withDefaultNamespace("world_list/join");
|
||||
static final ResourceLocation JOIN_HIGHLIGHTED_SPRITE = new ResourceLocation("world_list/join_highlighted");
|
||||
static final ResourceLocation JOIN_SPRITE = new ResourceLocation("world_list/join");
|
||||
static final Logger LOGGER = LogUtils.getLogger();
|
||||
+ private static final ResourceLocation FORGE_EXPERIMENTAL_WARNING_ICON = ResourceLocation.fromNamespaceAndPath("forge","textures/gui/experimental_warning.png");
|
||||
+ private static final ResourceLocation FORGE_EXPERIMENTAL_WARNING_ICON = new ResourceLocation("forge","textures/gui/experimental_warning.png");
|
||||
static final Component FROM_NEWER_TOOLTIP_1 = Component.translatable("selectWorld.tooltip.fromNewerVersion1").withStyle(ChatFormatting.RED);
|
||||
static final Component FROM_NEWER_TOOLTIP_2 = Component.translatable("selectWorld.tooltip.fromNewerVersion2").withStyle(ChatFormatting.RED);
|
||||
static final Component SNAPSHOT_TOOLTIP_1 = Component.translatable("selectWorld.tooltip.snapshot1").withStyle(ChatFormatting.GOLD);
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
--- a/net/minecraft/client/main/Main.java
|
||||
+++ b/net/minecraft/client/main/Main.java
|
||||
@@ -111,8 +_,8 @@
|
||||
CrashReport.preload();
|
||||
logger = LogUtils.getLogger();
|
||||
s1 = "Bootstrap";
|
||||
@@ -154,8 +_,8 @@
|
||||
CrashReport.preload();
|
||||
|
||||
try {
|
||||
- Bootstrap.bootStrap();
|
||||
GameLoadTimesEvent.INSTANCE.setBootstrapTime(Bootstrap.bootstrapDuration.get());
|
||||
+ net.minecraftforge.fml.loading.BackgroundWaiter.runAndTick(Bootstrap::bootStrap, net.minecraftforge.fml.loading.FMLLoader.progressWindowTick);
|
||||
Bootstrap.validate();
|
||||
s1 = "Argument parsing";
|
||||
List<String> list = optionset.valuesOf(optionspec27);
|
||||
} catch (Throwable throwable2) {
|
||||
CrashReport crashreport = CrashReport.forThrowable(throwable2, "Bootstrap");
|
||||
@@ -1,6 +1,6 @@
|
||||
--- a/net/minecraft/client/model/geom/LayerDefinitions.java
|
||||
+++ b/net/minecraft/client/model/geom/LayerDefinitions.java
|
||||
@@ -329,6 +_,7 @@
|
||||
@@ -328,6 +_,7 @@
|
||||
WoodType.values().forEach(p_171114_ -> builder.put(ModelLayers.createSignModelName(p_171114_), layerdefinition23));
|
||||
LayerDefinition layerdefinition24 = HangingSignRenderer.createHangingSignLayer();
|
||||
WoodType.values().forEach(p_247864_ -> builder.put(ModelLayers.createHangingSignModelName(p_247864_), layerdefinition24));
|
||||
|
||||
Loaded 100 of 1504 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user