diff --git a/build.gradle b/build.gradle index eeeb32442b..a5428fd46b 100644 --- a/build.gradle +++ b/build.gradle @@ -26,8 +26,8 @@ targetCompatibility = 1.8 archivesBaseName = "fabric" -def baseVersion = "0.2.2" -def mcVersion = "19w08a" +def baseVersion = "0.2.3" +def mcVersion = "19w08b" def ENV = System.getenv() version = baseVersion + "." + (ENV.BUILD_NUMBER ?: "local") diff --git a/src/main/java/net/fabricmc/fabric/api/entity/FabricEntityTypeBuilder.java b/src/main/java/net/fabricmc/fabric/api/entity/FabricEntityTypeBuilder.java index 3e50398c49..7b017a8165 100644 --- a/src/main/java/net/fabricmc/fabric/api/entity/FabricEntityTypeBuilder.java +++ b/src/main/java/net/fabricmc/fabric/api/entity/FabricEntityTypeBuilder.java @@ -16,9 +16,9 @@ package net.fabricmc.fabric.api.entity; -import net.minecraft.class_4048; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityCategory; +import net.minecraft.entity.EntitySize; import net.minecraft.entity.EntityType; import net.minecraft.world.World; import org.apache.logging.log4j.LogManager; @@ -36,15 +36,15 @@ public class FabricEntityTypeBuilder { private static final Logger LOGGER = LogManager.getLogger(); private final EntityCategory category; - private final EntityType.class_4049 function; + private final EntityType.class_4049 function; private boolean saveable = true; private boolean summonable = true; private int trackingDistance = -1; private int updateIntervalTicks = -1; private boolean alwaysUpdateVelocity = true; - private class_4048 size = class_4048.method_18385(-1.0f, -1.0f); + private EntitySize size = EntitySize.resizeable(-1.0f, -1.0f); - protected FabricEntityTypeBuilder(EntityCategory category, EntityType.class_4049 function) { + protected FabricEntityTypeBuilder(EntityCategory category, EntityType.class_4049 function) { this.category = category; this.function = function; } @@ -61,7 +61,7 @@ public static FabricEntityTypeBuilder create(EntityCategor return create(category, (t, w) -> function.apply(w)); } - public static FabricEntityTypeBuilder create(EntityCategory category, EntityType.class_4049 function) { + public static FabricEntityTypeBuilder create(EntityCategory category, EntityType.class_4049 function) { return new FabricEntityTypeBuilder<>(category, function); } @@ -76,15 +76,15 @@ public FabricEntityTypeBuilder disableSaving() { } /** - * @deprecated Use {@link FabricEntityTypeBuilder#size(class_4048)} + * @deprecated Use {@link FabricEntityTypeBuilder#size(EntitySize)} */ @Deprecated public FabricEntityTypeBuilder size(float width, float height) { - this.size = class_4048.method_18385(width, height); + this.size = EntitySize.resizeable(width, height); return this; } - public FabricEntityTypeBuilder size(class_4048 size) { + public FabricEntityTypeBuilder size(EntitySize size) { this.size = size; return this; } diff --git a/src/main/java/net/fabricmc/fabric/impl/resources/DeferredInputStream.java b/src/main/java/net/fabricmc/fabric/impl/resources/DeferredInputStream.java new file mode 100644 index 0000000000..762a889ef9 --- /dev/null +++ b/src/main/java/net/fabricmc/fabric/impl/resources/DeferredInputStream.java @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2016, 2017, 2018 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.fabric.impl.resources; + +import net.minecraft.util.Void; + +import java.io.IOException; +import java.io.InputStream; +import java.util.concurrent.*; + +/** + * InputStream deferring to a separate I/O thread to work around + * Thread.interrupt()-related issues in NIO. + */ +class DeferredInputStream extends InputStream { + private final InputStream stream; + + public static InputStream deferIfNeeded(Callable streamSupplier) throws IOException { + if (DeferredNioExecutionHandler.shouldDefer()) { + return new DeferredInputStream(streamSupplier); + } else { + return DeferredNioExecutionHandler.submit(streamSupplier, false); + } + } + + DeferredInputStream(Callable streamSupplier) throws IOException { + stream = DeferredNioExecutionHandler.submit(streamSupplier); + if (stream == null) { + throw new IOException("Something happened while trying to create an InputStream!"); + } + } + + DeferredInputStream(InputStream stream) throws IOException { + this.stream = stream; + if (this.stream == null) { + throw new IOException("Something happened while trying to create an InputStream!"); + } + } + + @Override + public int available() throws IOException { + return DeferredNioExecutionHandler.submit(stream::available); + } + + @Override + public boolean markSupported() { + return stream.markSupported(); + } + + @Override + public void mark(int readLimit) { + stream.mark(readLimit); + } + + @Override + public void reset() throws IOException { + DeferredNioExecutionHandler.submit(() -> { + stream.reset(); + return Void.INSTANCE; + }); + } + + @Override + public long skip(long n) throws IOException { + return DeferredNioExecutionHandler.submit(() -> stream.skip(n)); + } + + @Override + public int read() throws IOException { + return DeferredNioExecutionHandler.submit(stream::read); + } + + @Override + public int read(byte[] b) throws IOException { + return DeferredNioExecutionHandler.submit(() -> stream.read(b)); + } + + @Override + public int read(byte[] b, int offset, int length) throws IOException { + return DeferredNioExecutionHandler.submit(() -> stream.read(b, offset, length)); + } + + @Override + public void close() throws IOException { + DeferredNioExecutionHandler.submit(() -> { + stream.close(); + return Void.INSTANCE; + }); + } +} diff --git a/src/main/java/net/fabricmc/fabric/impl/resources/DeferredNioExecutionHandler.java b/src/main/java/net/fabricmc/fabric/impl/resources/DeferredNioExecutionHandler.java new file mode 100644 index 0000000000..065aed8943 --- /dev/null +++ b/src/main/java/net/fabricmc/fabric/impl/resources/DeferredNioExecutionHandler.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2016, 2017, 2018 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.fabric.impl.resources; + +import com.google.common.util.concurrent.ThreadFactoryBuilder; + +import java.io.IOException; +import java.util.concurrent.*; + +class DeferredNioExecutionHandler { + private static final ThreadLocal DEFERRED_REQUIRED = new ThreadLocal<>(); + private static ExecutorService EXECUTOR_SERVICE; + + public static boolean shouldDefer() { + Boolean deferRequired = DEFERRED_REQUIRED.get(); + if (deferRequired == null) { + deferRequired = false; + + StackTraceElement[] elements = Thread.currentThread().getStackTrace(); + for (int i = 0; i < elements.length; i++) { + if (elements[i].getClassName().startsWith("paulscode.sound.")) { + deferRequired = true; + break; + } + } + + DEFERRED_REQUIRED.set(deferRequired); + } + + return deferRequired; + } + + public static V submit(Callable callable, boolean cond) throws IOException { + try { + return cond ? submit(callable) : callable.call(); + } catch (IOException e) { + throw e; + } catch (Exception e) { + throw new RuntimeException("Exception which should not happen!", e); + } + } + + public static V submit(Callable callable) throws IOException { + if (EXECUTOR_SERVICE == null) { + EXECUTOR_SERVICE = Executors.newSingleThreadExecutor( + new ThreadFactoryBuilder() + .setNameFormat("Fabric Deferred I/O Thread") + .build() + ); + } + + Future future = EXECUTOR_SERVICE.submit(callable); + return getSubmittedFuture(future); + } + + static V getSubmittedFuture(Future future) throws IOException { + while (true) { + try { + return future.get(); + } catch (ExecutionException e) { + Throwable t = e.getCause(); + if (t instanceof IOException) { + throw (IOException) t; + } else { + throw new RuntimeException("ExecutionException which should not happen!", t); + } + } catch (InterruptedException e) { + // keep calm, carry on... + } + } + } + +} + diff --git a/src/main/java/net/fabricmc/fabric/impl/resources/ModNioResourcePack.java b/src/main/java/net/fabricmc/fabric/impl/resources/ModNioResourcePack.java index 25f3d235ee..0cd2015f68 100644 --- a/src/main/java/net/fabricmc/fabric/impl/resources/ModNioResourcePack.java +++ b/src/main/java/net/fabricmc/fabric/impl/resources/ModNioResourcePack.java @@ -16,18 +16,16 @@ package net.fabricmc.fabric.impl.resources; -import com.google.common.base.Joiner; import net.fabricmc.fabric.api.resource.ModResourcePack; import net.fabricmc.loader.api.metadata.ModMetadata; import net.minecraft.resource.AbstractFilenameResourcePack; -import net.minecraft.resource.ResourceNotFoundException; import net.minecraft.resource.ResourceType; import net.minecraft.util.Identifier; import net.minecraft.util.InvalidIdentifierException; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import java.io.File; +import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.nio.file.DirectoryStream; @@ -36,7 +34,6 @@ import java.util.*; import java.util.function.Predicate; import java.util.regex.Pattern; -import java.util.stream.Collectors; public class ModNioResourcePack extends AbstractFilenameResourcePack implements ModResourcePack { private static final Logger LOGGER = LogManager.getLogger(); @@ -68,16 +65,34 @@ private Path getPath(String filename) { @Override protected InputStream openFilename(String filename) throws IOException { - Path path = getPath(filename); - if (path != null && Files.isRegularFile(path)) { - return Files.newInputStream(path); + InputStream stream; + + if (DeferredNioExecutionHandler.shouldDefer()) { + stream = DeferredNioExecutionHandler.submit(() -> { + Path path = getPath(filename); + if (path != null && Files.isRegularFile(path)) { + return new DeferredInputStream(Files.newInputStream(path)); + } else { + return null; + } + }); + if (stream != null) { + return stream; + } + } else { + Path path = getPath(filename); + if (path != null && Files.isRegularFile(path)) { + return Files.newInputStream(path); + } } - InputStream stream = ModResourcePackUtil.openDefault(modInfo, filename); - if (stream == null) { - throw new ResourceNotFoundException(this.base, filename); + stream = ModResourcePackUtil.openDefault(modInfo, filename); + if (stream != null) { + return stream; } - return stream; + + // ReloadableResourceManagerImpl gets away with FileNotFoundException. + throw new FileNotFoundException("\"" + filename + "\" in Fabric mod \"" + modInfo.getId() + "\""); } @Override @@ -86,8 +101,19 @@ protected boolean containsFilename(String filename) { return true; } - Path path = getPath(filename); - return path != null && Files.isRegularFile(path); + if (DeferredNioExecutionHandler.shouldDefer()) { + try { + return DeferredNioExecutionHandler.submit(() -> { + Path path = getPath(filename); + return path != null && Files.isRegularFile(path); + }); + } catch (IOException e) { + return false; + } + } else { + Path path = getPath(filename); + return path != null && Files.isRegularFile(path); + } } @Override diff --git a/src/main/java/net/fabricmc/fabric/impl/resources/ModResourcePackUtil.java b/src/main/java/net/fabricmc/fabric/impl/resources/ModResourcePackUtil.java index 4c6b515f35..57c07b976c 100644 --- a/src/main/java/net/fabricmc/fabric/impl/resources/ModResourcePackUtil.java +++ b/src/main/java/net/fabricmc/fabric/impl/resources/ModResourcePackUtil.java @@ -62,7 +62,7 @@ public static InputStream openDefault(ModMetadata info, String filename) { case "pack.png": return ModResourcePackUtil.class.getClassLoader().getResourceAsStream("assets/fabric/textures/misc/default_icon.png"); case "pack.mcmeta": - String description = info.getId(); // TODO getName + String description = info.getName(); if (description == null) { description = ""; } else { @@ -71,15 +71,15 @@ public static InputStream openDefault(ModMetadata info, String filename) { String pack = String.format("{\"pack\":{\"pack_format\":" + PACK_FORMAT_VERSION + ",\"description\":\"%s\"}}", description); return IOUtils.toInputStream(pack, Charsets.UTF_8); default: - throw new RuntimeException("Mismatch with .containsDefault(...)!"); + return null; } } public static String getName(ModMetadata info) { -/* if (info.getName() != null) { + if (info.getName() != null) { return info.getName(); - } else */ { // TODO getName - return "Fabric Mod \"" + info.getId() + "\""; + } else { + return "Fabric Mod \"" + info.getId() + "\""; } } } diff --git a/src/main/java/net/fabricmc/fabric/mixin/resources/MixinMinecraftGame.java b/src/main/java/net/fabricmc/fabric/mixin/resources/MixinMinecraftGame.java index d0ea7552e6..f473542140 100644 --- a/src/main/java/net/fabricmc/fabric/mixin/resources/MixinMinecraftGame.java +++ b/src/main/java/net/fabricmc/fabric/mixin/resources/MixinMinecraftGame.java @@ -16,6 +16,7 @@ package net.fabricmc.fabric.mixin.resources; +import com.google.common.base.Joiner; import com.google.common.collect.Lists; import net.fabricmc.fabric.impl.resources.ModResourcePackUtil; import net.minecraft.client.MinecraftClient; @@ -33,6 +34,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import org.spongepowered.asm.mixin.injection.callback.LocalCapture; +import java.util.ArrayList; import java.util.List; import java.util.concurrent.CompletableFuture; @@ -44,22 +46,33 @@ public class MixinMinecraftGame { private void fabric_modifyResourcePackList(List list) { List oldList = Lists.newArrayList(list); list.clear(); + + boolean appended = false; for (int i = 0; i < oldList.size(); i++) { ResourcePack pack = oldList.get(i); list.add(pack); if (pack instanceof DefaultClientResourcePack) { ModResourcePackUtil.appendModResourcePacks(list, ResourceType.ASSETS); + appended = true; + } + } + + if (!appended) { + StringBuilder builder = new StringBuilder("Fabric could not find resource pack injection location!"); + for (ResourcePack rp : oldList) { + builder.append("\n - ").append(rp.getName()).append(" (").append(rp.getClass().getName()).append(")"); } + throw new RuntimeException(builder.toString()); } } - @Inject(method = "init", at = @At(value = "INVOKE", target = "Ljava/util/List;iterator()Ljava/util/Iterator;", ordinal = 0), locals = LocalCapture.CAPTURE_FAILHARD) + @Inject(method = "init", at = @At(value = "INVOKE", target = "Ljava/util/List;iterator()Ljava/util/Iterator;", ordinal = 0, shift = At.Shift.BY, by = -2), locals = LocalCapture.CAPTURE_FAILHARD) public void initResources(CallbackInfo info, List list) { fabric_modifyResourcePackList(list); } - @Inject(method = "reloadResources", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/MinecraftClient;method_18502(Lnet/minecraft/class_4071;)V", ordinal = 0), locals = LocalCapture.CAPTURE_FAILHARD) + @Inject(method = "reloadResources", at = @At(value = "INVOKE", target = "Lnet/minecraft/resource/ReloadableResourceManager;method_18232(Ljava/util/concurrent/Executor;Ljava/util/concurrent/Executor;Ljava/util/concurrent/CompletableFuture;Ljava/util/List;)Lnet/minecraft/resource/ResourceReloadHandler;", ordinal = 0), locals = LocalCapture.CAPTURE_FAILHARD) public void reloadResources(CallbackInfoReturnable info, CompletableFuture cf, List list) { fabric_modifyResourcePackList(list); } diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index 9ec5372588..2e3a1fd77e 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -1,7 +1,7 @@ { "id": "fabric", "name": "Fabric API", - "version": "0.2.2", + "version": "0.2.3", "side": "universal", "description": "Core API module providing key hooks and intercompatibility features.", "license": "Apache-2.0",