mirror of
https://github.com/FabricMC/fabric.git
synced 2025-07-29 15:39:55 -04:00
Merge branch 'master' into snapshot/1.14.3
This commit is contained in:
commit
10d8dc0969
54 changed files with 1472 additions and 203 deletions
fabric-content-registries-v0/src/main/java/net/fabricmc/fabric
fabric-loot-tables-v1
build.gradle
src/main
java/net/fabricmc/fabric
api/loot/v1
FabricLootPool.javaFabricLootPoolBuilder.javaFabricLootSupplier.javaFabricLootSupplierBuilder.javaLootEntryTypeRegistry.javaLootJsonParser.java
event
impl/loot
mixin/loot
resources
fabric-registry-sync-v0
build.gradle
src/main/java/net/fabricmc/fabric
impl/registry
mixin/registry
fabric-renderer-indigo
build.gradle
src/main/java/net/fabricmc/indigo
fabric-rendering-v0
fabric-tag-extensions-v0
fabric-testmods/java/net/fabricmc/fabric/loot
settings.gradle
|
@ -20,18 +20,13 @@ import net.fabricmc.fabric.impl.registry.LootEntryTypeRegistryImpl;
|
|||
import net.minecraft.world.loot.entry.LootEntry;
|
||||
|
||||
/**
|
||||
* Fabric's extensions to {@code net.minecraft.world.loot.entry.LootEntries} for registering
|
||||
* custom loot entry types.
|
||||
*
|
||||
* @see #register
|
||||
* @deprecated Use {@link net.fabricmc.fabric.api.loot.v1.LootEntryTypeRegistry}
|
||||
*/
|
||||
@Deprecated
|
||||
public interface LootEntryTypeRegistry {
|
||||
@Deprecated
|
||||
final LootEntryTypeRegistry INSTANCE = LootEntryTypeRegistryImpl.INSTANCE;
|
||||
|
||||
/**
|
||||
* Registers a loot entry type by its serializer.
|
||||
*
|
||||
* @param serializer the loot entry serializer
|
||||
*/
|
||||
@Deprecated
|
||||
void register(LootEntry.Serializer<?> serializer);
|
||||
}
|
||||
|
|
|
@ -21,10 +21,9 @@ import net.minecraft.world.loot.entry.LootEntries;
|
|||
import net.minecraft.world.loot.entry.LootEntry;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@Deprecated
|
||||
public final class LootEntryTypeRegistryImpl implements LootEntryTypeRegistry {
|
||||
private static Consumer<LootEntry.Serializer<?>> registerFunction;
|
||||
public static final LootEntryTypeRegistryImpl INSTANCE = new LootEntryTypeRegistryImpl();
|
||||
private static final Method REGISTER_METHOD;
|
||||
|
||||
|
|
6
fabric-loot-tables-v1/build.gradle
Normal file
6
fabric-loot-tables-v1/build.gradle
Normal file
|
@ -0,0 +1,6 @@
|
|||
archivesBaseName = "fabric-loot-tables-v1"
|
||||
version = "0.1.0"
|
||||
|
||||
dependencies {
|
||||
compile project(path: ':fabric-api-base', configuration: 'dev')
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 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.api.loot.v1;
|
||||
|
||||
import net.minecraft.world.loot.LootPool;
|
||||
import net.minecraft.world.loot.LootTableRange;
|
||||
import net.minecraft.world.loot.condition.LootCondition;
|
||||
import net.minecraft.world.loot.entry.LootEntry;
|
||||
import net.minecraft.world.loot.function.LootFunction;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* An interface implemented by all {@code net.minecraft.world.loot.LootPool} instances when
|
||||
* Fabric API is present. Contains accessors for various fields.
|
||||
*/
|
||||
public interface FabricLootPool {
|
||||
default LootPool asVanilla() {
|
||||
return (LootPool) this;
|
||||
}
|
||||
List<LootEntry> getEntries();
|
||||
List<LootCondition> getConditions();
|
||||
List<LootFunction> getFunctions();
|
||||
LootTableRange getRollsRange();
|
||||
}
|
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 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.api.loot.v1;
|
||||
|
||||
import net.fabricmc.fabric.mixin.loot.LootPoolBuilderHooks;
|
||||
import net.minecraft.world.loot.LootPool;
|
||||
import net.minecraft.world.loot.LootTableRange;
|
||||
import net.minecraft.world.loot.condition.LootCondition;
|
||||
import net.minecraft.world.loot.entry.LootEntry;
|
||||
import net.minecraft.world.loot.function.LootFunction;
|
||||
|
||||
public class FabricLootPoolBuilder extends LootPool.Builder {
|
||||
private final LootPoolBuilderHooks extended = (LootPoolBuilderHooks) this;
|
||||
|
||||
private FabricLootPoolBuilder() {}
|
||||
|
||||
private FabricLootPoolBuilder(LootPool pool) {
|
||||
copyFrom(pool, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FabricLootPoolBuilder withRolls(LootTableRange range) {
|
||||
super.withRolls(range);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FabricLootPoolBuilder withEntry(LootEntry.Builder<?> entry) {
|
||||
super.withEntry(entry);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FabricLootPoolBuilder withCondition(LootCondition.Builder condition) {
|
||||
super.method_356(condition);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FabricLootPoolBuilder withFunction(LootFunction.Builder function) {
|
||||
super.method_353(function);
|
||||
return this;
|
||||
}
|
||||
|
||||
public FabricLootPoolBuilder withEntry(LootEntry entry) {
|
||||
extended.getEntries().add(entry);
|
||||
return this;
|
||||
}
|
||||
|
||||
public FabricLootPoolBuilder withCondition(LootCondition condition) {
|
||||
extended.getConditions().add(condition);
|
||||
return this;
|
||||
}
|
||||
|
||||
public FabricLootPoolBuilder withFunction(LootFunction function) {
|
||||
extended.getFunctions().add(function);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies the entries, conditions and functions of the {@code pool} to this
|
||||
* builder.
|
||||
*
|
||||
* This is equal to {@code copyFrom(pool, false)}.
|
||||
*/
|
||||
public FabricLootPoolBuilder copyFrom(LootPool pool) {
|
||||
return copyFrom(pool, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies the entries, conditions and functions of the {@code pool} to this
|
||||
* builder.
|
||||
*
|
||||
* If {@code copyRolls} is true, the {@link FabricLootPool#getRollsRange rolls} of the pool are also copied.
|
||||
*/
|
||||
public FabricLootPoolBuilder copyFrom(LootPool pool, boolean copyRolls) {
|
||||
FabricLootPool extendedPool = (FabricLootPool) pool;
|
||||
extended.getConditions().addAll(extendedPool.getConditions());
|
||||
extended.getFunctions().addAll(extendedPool.getFunctions());
|
||||
extended.getEntries().addAll(extendedPool.getEntries());
|
||||
|
||||
if (copyRolls) {
|
||||
withRolls(extendedPool.getRollsRange());
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public static FabricLootPoolBuilder builder() {
|
||||
return new FabricLootPoolBuilder();
|
||||
}
|
||||
|
||||
public static FabricLootPoolBuilder of(LootPool pool) {
|
||||
return new FabricLootPoolBuilder(pool);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 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.api.loot.v1;
|
||||
|
||||
import net.minecraft.world.loot.LootPool;
|
||||
import net.minecraft.world.loot.LootSupplier;
|
||||
import net.minecraft.world.loot.context.LootContextType;
|
||||
import net.minecraft.world.loot.function.LootFunction;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* An interface implemented by all {@code net.minecraft.world.loot.LootSupplier} instances when
|
||||
* Fabric API is present. Contains accessors for various fields.
|
||||
*/
|
||||
public interface FabricLootSupplier {
|
||||
default LootSupplier asVanilla() {
|
||||
return (LootSupplier) this;
|
||||
}
|
||||
List<LootPool> getPools();
|
||||
List<LootFunction> getFunctions();
|
||||
LootContextType getType();
|
||||
}
|
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 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.api.loot.v1;
|
||||
|
||||
import net.fabricmc.fabric.mixin.loot.LootSupplierBuilderHooks;
|
||||
import net.minecraft.world.loot.LootPool;
|
||||
import net.minecraft.world.loot.LootSupplier;
|
||||
import net.minecraft.world.loot.context.LootContextType;
|
||||
import net.minecraft.world.loot.function.LootFunction;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
public class FabricLootSupplierBuilder extends LootSupplier.Builder {
|
||||
private final LootSupplierBuilderHooks extended = (LootSupplierBuilderHooks) this;
|
||||
|
||||
protected FabricLootSupplierBuilder() {}
|
||||
|
||||
private FabricLootSupplierBuilder(LootSupplier supplier) {
|
||||
copyFrom(supplier, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FabricLootSupplierBuilder withPool(LootPool.Builder pool) {
|
||||
super.withPool(pool);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FabricLootSupplierBuilder withType(LootContextType type) {
|
||||
super.withType(type);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FabricLootSupplierBuilder withFunction(LootFunction.Builder function) {
|
||||
super.method_335(function);
|
||||
return this;
|
||||
}
|
||||
|
||||
public FabricLootSupplierBuilder withPool(LootPool pool) {
|
||||
extended.getPools().add(pool);
|
||||
return this;
|
||||
}
|
||||
|
||||
public FabricLootSupplierBuilder withFunction(LootFunction function) {
|
||||
extended.getFunctions().add(function);
|
||||
return this;
|
||||
}
|
||||
|
||||
public FabricLootSupplierBuilder withPools(Collection<LootPool> pools) {
|
||||
pools.forEach(this::withPool);
|
||||
return this;
|
||||
}
|
||||
|
||||
public FabricLootSupplierBuilder withFunctions(Collection<LootFunction> functions) {
|
||||
functions.forEach(this::withFunction);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies the pools and functions of the {@code supplier} to this builder.
|
||||
* This is equal to {@code copyFrom(supplier, false)}.
|
||||
*/
|
||||
public FabricLootSupplierBuilder copyFrom(LootSupplier supplier) {
|
||||
return copyFrom(supplier, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies the pools and functions of the {@code supplier} to this builder.
|
||||
* If {@code copyType} is true, the {@link FabricLootSupplier#getType type} of the supplier is also copied.
|
||||
*/
|
||||
public FabricLootSupplierBuilder copyFrom(LootSupplier supplier, boolean copyType) {
|
||||
FabricLootSupplier extendedSupplier = (FabricLootSupplier) supplier;
|
||||
extended.getPools().addAll(extendedSupplier.getPools());
|
||||
extended.getFunctions().addAll(extendedSupplier.getFunctions());
|
||||
|
||||
if (copyType) {
|
||||
withType(extendedSupplier.getType());
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public static FabricLootSupplierBuilder builder() {
|
||||
return new FabricLootSupplierBuilder();
|
||||
}
|
||||
|
||||
public static FabricLootSupplierBuilder of(LootSupplier supplier) {
|
||||
return new FabricLootSupplierBuilder(supplier);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 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.api.loot.v1;
|
||||
|
||||
import net.fabricmc.fabric.impl.loot.LootEntryTypeRegistryImpl;
|
||||
import net.minecraft.world.loot.entry.LootEntry;
|
||||
|
||||
/**
|
||||
* Fabric's extensions to {@code net.minecraft.world.loot.entry.LootEntries} for registering
|
||||
* custom loot entry types.
|
||||
*
|
||||
* @see #register
|
||||
*/
|
||||
public interface LootEntryTypeRegistry {
|
||||
final LootEntryTypeRegistry INSTANCE = LootEntryTypeRegistryImpl.INSTANCE;
|
||||
|
||||
/**
|
||||
* Registers a loot entry type by its serializer.
|
||||
*
|
||||
* @param serializer the loot entry serializer
|
||||
*/
|
||||
void register(LootEntry.Serializer<?> serializer);
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 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.api.loot.v1;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import net.minecraft.resource.Resource;
|
||||
import net.minecraft.resource.ResourceManager;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.JsonHelper;
|
||||
import net.minecraft.util.Lazy;
|
||||
import net.minecraft.world.loot.*;
|
||||
import net.minecraft.world.loot.condition.LootCondition;
|
||||
import net.minecraft.world.loot.entry.LootEntry;
|
||||
import net.minecraft.world.loot.function.LootFunction;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
import java.lang.reflect.Field;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public final class LootJsonParser {
|
||||
/* Reading this from LootManager to access all serializers from vanilla. */
|
||||
private static final Lazy<Gson> GSON = new Lazy<>(() -> {
|
||||
try {
|
||||
Field gsonField = Stream.of(LootManager.class.getDeclaredFields())
|
||||
.filter(field -> field.getType() == Gson.class)
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new RuntimeException("Gson not found in LootManager!"));
|
||||
gsonField.setAccessible(true);
|
||||
return (Gson) gsonField.get(null);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Exception while getting Gson instance from LootManager", e);
|
||||
}
|
||||
});
|
||||
|
||||
private LootJsonParser() {
|
||||
|
||||
}
|
||||
|
||||
public static <T> T read(Reader json, Class<T> c) {
|
||||
return JsonHelper.deserialize(GSON.get(), json, c);
|
||||
}
|
||||
|
||||
public static <T> T read(String json, Class<T> c) {
|
||||
return JsonHelper.deserialize(GSON.get(), json, c);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 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.api.loot.v1.event;
|
||||
|
||||
import net.fabricmc.fabric.api.event.Event;
|
||||
import net.fabricmc.fabric.api.event.EventFactory;
|
||||
import net.fabricmc.fabric.api.loot.v1.FabricLootSupplierBuilder;
|
||||
import net.minecraft.resource.ResourceManager;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.world.loot.LootManager;
|
||||
import net.minecraft.world.loot.LootSupplier;
|
||||
|
||||
/**
|
||||
* An event handler that is called when loot tables are loaded.
|
||||
* Use {@link #EVENT} to register instances.
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface LootTableLoadingCallback {
|
||||
@FunctionalInterface
|
||||
interface LootTableSetter {
|
||||
void set(LootSupplier supplier);
|
||||
}
|
||||
|
||||
final Event<LootTableLoadingCallback> EVENT = EventFactory.createArrayBacked(
|
||||
LootTableLoadingCallback.class,
|
||||
(listeners) -> (resourceManager, manager, id, supplier, setter) -> {
|
||||
for (LootTableLoadingCallback callback : listeners) {
|
||||
callback.onLootTableLoading(resourceManager, manager, id, supplier, setter);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
void onLootTableLoading(ResourceManager resourceManager, LootManager manager, Identifier id, FabricLootSupplierBuilder supplier, LootTableSetter setter);
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 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.loot;
|
||||
|
||||
import net.minecraft.world.loot.entry.LootEntries;
|
||||
import net.minecraft.world.loot.entry.LootEntry;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
public final class LootEntryTypeRegistryImpl implements net.fabricmc.fabric.api.loot.v1.LootEntryTypeRegistry {
|
||||
public static final LootEntryTypeRegistryImpl INSTANCE = new LootEntryTypeRegistryImpl();
|
||||
private static final Method REGISTER_METHOD;
|
||||
|
||||
static {
|
||||
Method target = null;
|
||||
for (Method m : LootEntries.class.getDeclaredMethods()) {
|
||||
if (m.getParameterCount() == 1 && m.getParameterTypes()[0] == LootEntry.Serializer.class) {
|
||||
if (target != null) {
|
||||
throw new RuntimeException("More than one register-like method found in LootEntries!");
|
||||
} else {
|
||||
target = m;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (target == null) {
|
||||
throw new RuntimeException("Could not find register-like method in LootEntries!");
|
||||
} else {
|
||||
REGISTER_METHOD = target;
|
||||
REGISTER_METHOD.setAccessible(true);
|
||||
}
|
||||
}
|
||||
|
||||
private LootEntryTypeRegistryImpl() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void register(LootEntry.Serializer<?> serializer) {
|
||||
try {
|
||||
REGISTER_METHOD.invoke(null, serializer);
|
||||
} catch (Throwable t) {
|
||||
throw new RuntimeException(t);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 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.mixin.loot;
|
||||
|
||||
import net.minecraft.world.loot.LootPool;
|
||||
import net.minecraft.world.loot.condition.LootCondition;
|
||||
import net.minecraft.world.loot.entry.LootEntry;
|
||||
import net.minecraft.world.loot.function.LootFunction;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mixin(LootPool.Builder.class)
|
||||
public interface LootPoolBuilderHooks {
|
||||
@Accessor
|
||||
List<LootEntry> getEntries();
|
||||
@Accessor
|
||||
List<LootCondition> getConditions();
|
||||
@Accessor
|
||||
List<LootFunction> getFunctions();
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 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.mixin.loot;
|
||||
|
||||
import net.minecraft.world.loot.LootPool;
|
||||
import net.minecraft.world.loot.LootSupplier;
|
||||
import net.minecraft.world.loot.function.LootFunction;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mixin(LootSupplier.Builder.class)
|
||||
public interface LootSupplierBuilderHooks {
|
||||
@Accessor
|
||||
List<LootPool> getPools();
|
||||
@Accessor
|
||||
List<LootFunction> getFunctions();
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 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.mixin.loot;
|
||||
|
||||
import net.fabricmc.fabric.api.loot.v1.event.LootTableLoadingCallback;
|
||||
import net.fabricmc.fabric.api.loot.v1.FabricLootSupplierBuilder;
|
||||
import net.minecraft.resource.ResourceManager;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.world.loot.LootManager;
|
||||
import net.minecraft.world.loot.LootSupplier;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Mixin(LootManager.class)
|
||||
public class MixinLootManager {
|
||||
@Shadow @Final private Map<Identifier, LootSupplier> suppliers;
|
||||
|
||||
@Inject(method = "apply", at = @At("RETURN"))
|
||||
private void apply(ResourceManager manager, CallbackInfo info) {
|
||||
Map<Identifier, LootSupplier> newSuppliers = new HashMap<>();
|
||||
|
||||
suppliers.forEach((id, supplier) -> {
|
||||
FabricLootSupplierBuilder builder = FabricLootSupplierBuilder.of(supplier);
|
||||
|
||||
//noinspection ConstantConditions
|
||||
LootTableLoadingCallback.EVENT.invoker().onLootTableLoading(
|
||||
manager, (LootManager) (Object) this, id, builder, (s) -> newSuppliers.put(id, s)
|
||||
);
|
||||
|
||||
newSuppliers.computeIfAbsent(id, (i) -> builder.create());
|
||||
});
|
||||
|
||||
for (Identifier id : newSuppliers.keySet()) {
|
||||
suppliers.put(id, newSuppliers.get(id));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 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.mixin.loot;
|
||||
|
||||
import net.fabricmc.fabric.api.loot.v1.FabricLootPool;
|
||||
import net.minecraft.world.loot.LootPool;
|
||||
import net.minecraft.world.loot.LootTableRange;
|
||||
import net.minecraft.world.loot.condition.LootCondition;
|
||||
import net.minecraft.world.loot.entry.LootEntry;
|
||||
import net.minecraft.world.loot.function.LootFunction;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
@Mixin(LootPool.class)
|
||||
public abstract class MixinLootPool implements FabricLootPool {
|
||||
@Shadow @Final private LootEntry[] entries;
|
||||
|
||||
@Shadow @Final private LootCondition[] conditions;
|
||||
|
||||
@Shadow @Final private LootFunction[] functions;
|
||||
|
||||
@Override
|
||||
public List<LootEntry> getEntries() {
|
||||
return Arrays.asList(entries);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<LootCondition> getConditions() {
|
||||
return Arrays.asList(conditions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<LootFunction> getFunctions() {
|
||||
return Arrays.asList(functions);
|
||||
}
|
||||
|
||||
@Accessor
|
||||
@Override
|
||||
public abstract LootTableRange getRollsRange();
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 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.mixin.loot;
|
||||
|
||||
import net.fabricmc.fabric.api.loot.v1.FabricLootSupplier;
|
||||
import net.minecraft.world.loot.LootPool;
|
||||
import net.minecraft.world.loot.LootSupplier;
|
||||
import net.minecraft.world.loot.context.LootContextType;
|
||||
import net.minecraft.world.loot.function.LootFunction;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
@Mixin(LootSupplier.class)
|
||||
public abstract class MixinLootSupplier implements FabricLootSupplier {
|
||||
@Shadow @Final private LootPool[] pools;
|
||||
@Shadow @Final private LootFunction[] functions;
|
||||
|
||||
@Override
|
||||
public List<LootPool> getPools() {
|
||||
return Arrays.asList(pools);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<LootFunction> getFunctions() {
|
||||
return Arrays.asList(functions);
|
||||
}
|
||||
|
||||
@Accessor
|
||||
@Override
|
||||
public abstract LootContextType getType();
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"required": true,
|
||||
"package": "net.fabricmc.fabric.mixin.loot",
|
||||
"compatibilityLevel": "JAVA_8",
|
||||
"mixins": [
|
||||
"LootPoolBuilderHooks",
|
||||
"LootSupplierBuilderHooks",
|
||||
"MixinLootManager",
|
||||
"MixinLootPool",
|
||||
"MixinLootSupplier"
|
||||
],
|
||||
"injectors": {
|
||||
"defaultRequire": 1
|
||||
}
|
||||
}
|
13
fabric-loot-tables-v1/src/main/resources/fabric.mod.json
Normal file
13
fabric-loot-tables-v1/src/main/resources/fabric.mod.json
Normal file
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "fabric-loot-tables-v1",
|
||||
"version": "${version}",
|
||||
"license": "Apache-2.0",
|
||||
"depends": {
|
||||
"fabricloader": ">=0.4.0",
|
||||
"fabric-api-base": "*"
|
||||
},
|
||||
"mixins": [
|
||||
"fabric-loot-tables-v1.mixins.json"
|
||||
]
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
archivesBaseName = "fabric-registry-sync-v0"
|
||||
version = getSubprojectVersion(project, "0.2.1")
|
||||
version = getSubprojectVersion(project, "0.2.2")
|
||||
|
||||
dependencies {
|
||||
compile project(path: ':fabric-api-base', configuration: 'dev')
|
||||
|
|
|
@ -31,7 +31,7 @@ public class FabricRegistryClientInit implements ClientModInitializer {
|
|||
public void onInitializeClient() {
|
||||
ClientSidePacketRegistry.INSTANCE.register(RegistrySyncManager.ID, (ctx, buf) -> {
|
||||
// if not hosting server, apply packet
|
||||
RegistrySyncManager.receivePacket(ctx, buf, !MinecraftClient.getInstance().isInSingleplayer(), (e) -> {
|
||||
RegistrySyncManager.receivePacket(ctx, buf, RegistrySyncManager.DEBUG || !MinecraftClient.getInstance().isInSingleplayer(), (e) -> {
|
||||
LOGGER.error("Registry remapping failed!", e);
|
||||
MinecraftClient.getInstance().execute(() -> {
|
||||
((ClientPlayerEntity) ctx.getPlayer()).networkHandler.getClientConnection().disconnect(
|
||||
|
|
|
@ -16,8 +16,12 @@
|
|||
|
||||
package net.fabricmc.fabric.impl.registry;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Sets;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.ints.IntSet;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
|
||||
import net.fabricmc.fabric.api.network.PacketContext;
|
||||
|
@ -29,7 +33,13 @@ import net.minecraft.util.PacketByteBuf;
|
|||
import net.minecraft.util.registry.MutableRegistry;
|
||||
import net.minecraft.util.registry.Registry;
|
||||
import net.minecraft.util.registry.SimpleRegistry;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
@ -37,7 +47,10 @@ import java.util.concurrent.TimeoutException;
|
|||
import java.util.function.Consumer;
|
||||
|
||||
public final class RegistrySyncManager {
|
||||
public static final Identifier ID = new Identifier("fabric", "registry/sync");
|
||||
static final boolean DEBUG = System.getProperty("fabric.registry.debug", "false").equalsIgnoreCase("true");
|
||||
static final Identifier ID = new Identifier("fabric", "registry/sync");
|
||||
private static final Logger LOGGER = LogManager.getLogger();
|
||||
private static final boolean DEBUG_WRITE_REGISTRY_DATA = System.getProperty("fabric.registry.debug.writeContentsAsCsv", "false").equalsIgnoreCase("true");
|
||||
private static final Set<Identifier> REGISTRY_BLACKLIST = ImmutableSet.of();
|
||||
private static final Set<Identifier> REGISTRY_BLACKLIST_NETWORK = ImmutableSet.of();
|
||||
|
||||
|
@ -80,6 +93,39 @@ public final class RegistrySyncManager {
|
|||
CompoundTag mainTag = new CompoundTag();
|
||||
|
||||
for (Identifier registryId : Registry.REGISTRIES.getIds()) {
|
||||
if (DEBUG_WRITE_REGISTRY_DATA) {
|
||||
File location = new File(".fabric" + File.separatorChar + "debug" + File.separatorChar + "registry");
|
||||
boolean c = true;
|
||||
if (!location.exists()) {
|
||||
if (!location.mkdirs()) {
|
||||
LOGGER.warn("[fabric-registry-sync debug] Could not create " + location.getAbsolutePath() + " directory!");
|
||||
c = false;
|
||||
}
|
||||
}
|
||||
|
||||
MutableRegistry registry = Registry.REGISTRIES.get(registryId);
|
||||
if (c && registry != null) {
|
||||
File file = new File(location, registryId.toString().replace(':', '.').replace('/', '.') + ".csv");
|
||||
try (FileOutputStream stream = new FileOutputStream(file)) {
|
||||
StringBuilder builder = new StringBuilder("Raw ID,String ID,Class Type\n");
|
||||
for (Object o : registry) {
|
||||
String classType = (o == null) ? "null" : o.getClass().getName();
|
||||
//noinspection unchecked
|
||||
Identifier id = registry.getId(o);
|
||||
if (id == null) continue;
|
||||
|
||||
//noinspection unchecked
|
||||
int rawId = registry.getRawId(o);
|
||||
String stringId = id.toString();
|
||||
builder.append("\"").append(rawId).append("\",\"").append(stringId).append("\",\"").append(classType).append("\"\n");
|
||||
}
|
||||
stream.write(builder.toString().getBytes(StandardCharsets.UTF_8));
|
||||
} catch (IOException e) {
|
||||
LOGGER.warn("[fabric-registry-sync debug] Could not write to " + file.getAbsolutePath() + "!", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (REGISTRY_BLACKLIST.contains(registryId)) {
|
||||
continue;
|
||||
} else if (isClientSync && REGISTRY_BLACKLIST_NETWORK.contains(registryId)) {
|
||||
|
@ -87,12 +133,35 @@ public final class RegistrySyncManager {
|
|||
}
|
||||
|
||||
MutableRegistry registry = Registry.REGISTRIES.get(registryId);
|
||||
if (registry instanceof SimpleRegistry && registry instanceof RemappableRegistry) {
|
||||
if (registry instanceof RemappableRegistry) {
|
||||
CompoundTag registryTag = new CompoundTag();
|
||||
//noinspection unchecked
|
||||
for (Identifier identifier : (Set<Identifier>) registry.getIds()) {
|
||||
registryTag.putInt(identifier.toString(), registry.getRawId(registry.get(identifier)));
|
||||
IntSet rawIdsFound = DEBUG ? new IntOpenHashSet() : null;
|
||||
|
||||
for (Object o : registry) {
|
||||
//noinspection unchecked
|
||||
Identifier id = registry.getId(o);
|
||||
if (id == null) continue;
|
||||
|
||||
//noinspection unchecked
|
||||
int rawId = registry.getRawId(o);
|
||||
|
||||
if (DEBUG) {
|
||||
if (registry.get(id) != o) {
|
||||
LOGGER.error("[fabric-registry-sync] Inconsistency detected in " + registryId + ": object " + o + " -> string ID " + id + " -> object " + registry.get(id) + "!");
|
||||
}
|
||||
|
||||
if (registry.get(rawId) != o) {
|
||||
LOGGER.error("[fabric-registry-sync] Inconsistency detected in " + registryId + ": object " + o + " -> integer ID " + rawId + " -> object " + registry.get(rawId) + "!");
|
||||
}
|
||||
|
||||
if (!rawIdsFound.add(rawId)) {
|
||||
LOGGER.error("[fabric-registry-sync] Inconsistency detected in " + registryId + ": multiple objects hold the raw ID " + rawId + " (this one is " + id + ")");
|
||||
}
|
||||
}
|
||||
|
||||
registryTag.putInt(id.toString(), rawId);
|
||||
}
|
||||
|
||||
mainTag.put(registryId.toString(), registryTag);
|
||||
}
|
||||
}
|
||||
|
@ -106,22 +175,29 @@ public final class RegistrySyncManager {
|
|||
|
||||
public static void apply(CompoundTag tag, RemappableRegistry.RemapMode mode) throws RemapException {
|
||||
CompoundTag mainTag = tag.getCompound("registries");
|
||||
Set<String> containedRegistries = Sets.newHashSet(mainTag.getKeys());
|
||||
|
||||
for (Identifier registryId : Registry.REGISTRIES.getIds()) {
|
||||
if (!mainTag.containsKey(registryId.toString())) {
|
||||
if (!containedRegistries.remove(registryId.toString())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
CompoundTag registryTag = mainTag.getCompound(registryId.toString());
|
||||
MutableRegistry registry = Registry.REGISTRIES.get(registryId);
|
||||
if (registry instanceof SimpleRegistry && registry instanceof RemappableRegistry) {
|
||||
|
||||
if (registry instanceof RemappableRegistry) {
|
||||
Object2IntMap<Identifier> idMap = new Object2IntOpenHashMap<>();
|
||||
for (String key : registryTag.getKeys()) {
|
||||
idMap.put(new Identifier(key), registryTag.getInt(key));
|
||||
}
|
||||
|
||||
((RemappableRegistry) registry).remap(registryId.toString(), idMap, mode);
|
||||
}
|
||||
}
|
||||
|
||||
if (!containedRegistries.isEmpty()) {
|
||||
LOGGER.warn("[fabric-registry-sync] Could not find the following registries: " + Joiner.on(", ").join(containedRegistries));
|
||||
}
|
||||
}
|
||||
|
||||
public static void unmap() throws RemapException {
|
||||
|
|
|
@ -25,10 +25,12 @@ import net.minecraft.util.registry.Registry;
|
|||
|
||||
public class RemapStateImpl<T> implements RegistryIdRemapCallback.RemapState<T> {
|
||||
private final Int2IntMap rawIdChangeMap;
|
||||
private final Int2ObjectMap<Identifier> oldIdMap;
|
||||
private final Int2ObjectMap<Identifier> newIdMap;
|
||||
|
||||
public RemapStateImpl(Registry<T> registry, Int2IntMap rawIdChangeMap) {
|
||||
public RemapStateImpl(Registry<T> registry, Int2ObjectMap<Identifier> oldIdMap, Int2IntMap rawIdChangeMap) {
|
||||
this.rawIdChangeMap = rawIdChangeMap;
|
||||
this.oldIdMap = oldIdMap;
|
||||
this.newIdMap = new Int2ObjectOpenHashMap<>();
|
||||
|
||||
for (Int2IntMap.Entry entry : rawIdChangeMap.int2IntEntrySet()) {
|
||||
|
@ -44,7 +46,7 @@ public class RemapStateImpl<T> implements RegistryIdRemapCallback.RemapState<T>
|
|||
|
||||
@Override
|
||||
public Identifier getIdFromOld(int oldRawId) {
|
||||
return newIdMap.get(rawIdChangeMap.getOrDefault(oldRawId, -1));
|
||||
return oldIdMap.get(oldRawId);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -28,15 +28,17 @@ import java.util.HashMap;
|
|||
import java.util.Map;
|
||||
|
||||
public class IdListTracker<V, OV> implements RegistryEntryAddedCallback<V>, RegistryIdRemapCallback<V>, RegistryEntryRemovedCallback<V> {
|
||||
private final String name;
|
||||
private final IdList<OV> mappers;
|
||||
private Map<Identifier, OV> removedMapperCache = new HashMap<>();
|
||||
|
||||
private IdListTracker(IdList<OV> mappers) {
|
||||
private IdListTracker(String name, IdList<OV> mappers) {
|
||||
this.name = name;
|
||||
this.mappers = mappers;
|
||||
}
|
||||
|
||||
public static <V, OV> void register(Registry<V> registry, IdList<OV> mappers) {
|
||||
IdListTracker<V, OV> updater = new IdListTracker<>(mappers);
|
||||
public static <V, OV> void register(Registry<V> registry, String name, IdList<OV> mappers) {
|
||||
IdListTracker<V, OV> updater = new IdListTracker<>(name, mappers);
|
||||
RegistryEntryAddedCallback.event(registry).register(updater);
|
||||
RegistryIdRemapCallback.event(registry).register(updater);
|
||||
RegistryEntryRemovedCallback.event(registry).register(updater);
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
package net.fabricmc.fabric.impl.registry.trackers;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import net.fabricmc.fabric.api.event.registry.RegistryEntryAddedCallback;
|
||||
|
@ -23,20 +25,27 @@ import net.fabricmc.fabric.api.event.registry.RegistryIdRemapCallback;
|
|||
import net.fabricmc.fabric.api.event.registry.RegistryEntryRemovedCallback;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.registry.Registry;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class Int2ObjectMapTracker<V, OV> implements RegistryEntryAddedCallback<V>, RegistryIdRemapCallback<V>, RegistryEntryRemovedCallback<V> {
|
||||
private static final Logger LOGGER = LogManager.getLogger();
|
||||
private final String name;
|
||||
private final Int2ObjectMap<OV> mappers;
|
||||
private Map<Identifier, OV> removedMapperCache = new HashMap<>();
|
||||
|
||||
private Int2ObjectMapTracker(Int2ObjectMap<OV> mappers) {
|
||||
private Int2ObjectMapTracker(String name, Int2ObjectMap<OV> mappers) {
|
||||
this.name = name;
|
||||
this.mappers = mappers;
|
||||
}
|
||||
|
||||
public static <V, OV> void register(Registry<V> registry, Int2ObjectMap<OV> mappers) {
|
||||
Int2ObjectMapTracker<V, OV> updater = new Int2ObjectMapTracker<>(mappers);
|
||||
public static <V, OV> void register(Registry<V> registry, String name, Int2ObjectMap<OV> mappers) {
|
||||
Int2ObjectMapTracker<V, OV> updater = new Int2ObjectMapTracker<>(name, mappers);
|
||||
RegistryEntryAddedCallback.event(registry).register(updater);
|
||||
RegistryIdRemapCallback.event(registry).register(updater);
|
||||
RegistryEntryRemovedCallback.event(registry).register(updater);
|
||||
|
@ -52,22 +61,38 @@ public class Int2ObjectMapTracker<V, OV> implements RegistryEntryAddedCallback<V
|
|||
@Override
|
||||
public void onRemap(RemapState<V> state) {
|
||||
Int2ObjectMap<OV> oldMappers = new Int2ObjectOpenHashMap<>(mappers);
|
||||
Int2IntMap remapMap = state.getRawIdChangeMap();
|
||||
List<String> errors = null;
|
||||
|
||||
mappers.clear();
|
||||
for (int i : oldMappers.keySet()) {
|
||||
int newI = state.getRawIdChangeMap().getOrDefault(i, i);
|
||||
if (mappers.containsKey(newI)) {
|
||||
throw new RuntimeException("Int2ObjectMap contained two equal IDs " + newI + " (" + state.getIdFromOld(i) + "/" + i + " -> " + state.getIdFromNew(newI) + "/" + newI + ")!");
|
||||
}
|
||||
int newI = remapMap.getOrDefault(i, Integer.MIN_VALUE);
|
||||
if (newI >= 0) {
|
||||
if (mappers.containsKey(newI)) {
|
||||
if (errors == null) {
|
||||
errors = new ArrayList<>();
|
||||
}
|
||||
|
||||
mappers.put(newI, oldMappers.get(i));
|
||||
errors.add(" - Map contained two equal IDs " + newI + " (" + state.getIdFromOld(i) + "/" + i + " -> " + state.getIdFromNew(newI) + "/" + newI + ")!");
|
||||
} else {
|
||||
mappers.put(newI, oldMappers.get(i));
|
||||
}
|
||||
} else {
|
||||
LOGGER.warn("[fabric-registry-sync] Int2ObjectMap " + name + " is dropping mapping for integer ID " + i + " (" + state.getIdFromOld(i) + ") - should not happen!");
|
||||
removedMapperCache.put(state.getIdFromOld(i), oldMappers.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
if (errors != null) {
|
||||
throw new RuntimeException("Errors while remapping Int2ObjectMap " + name + " found:\n" + Joiner.on('\n').join(errors));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEntryRemoved(int rawId, Identifier id, V object) {
|
||||
if (mappers.containsKey(rawId)) {
|
||||
removedMapperCache.put(id, mappers.remove(rawId));
|
||||
OV mapper = mappers.remove(rawId);
|
||||
if (mapper != null) {
|
||||
removedMapperCache.put(id, mapper);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -57,7 +57,7 @@ public final class StateIdTracker<T, S> implements RegistryIdRemapCallback<T>, R
|
|||
stateGetter.apply(object).forEach(stateList::add);
|
||||
currentHighestId = rawId;
|
||||
} else {
|
||||
logger.debug("[fabric-registry-sync] Non-sequential RegistryEntryAddedCallback for state ID tracker (at " + id + "), forcing state map recalculation...");
|
||||
logger.debug("[fabric-registry-sync] Non-sequential RegistryEntryAddedCallback for " + object.getClass().getSimpleName() + " ID tracker (at " + id + "), forcing state map recalculation...");
|
||||
recalcStateMap();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,8 +18,7 @@ package net.fabricmc.fabric.mixin.registry;
|
|||
|
||||
import com.google.common.collect.BiMap;
|
||||
import com.google.common.collect.HashBiMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.ints.*;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
|
||||
import net.fabricmc.fabric.api.event.Event;
|
||||
|
@ -116,12 +115,21 @@ public abstract class MixinIdRegistry<T> implements RemappableRegistry, Listenab
|
|||
@SuppressWarnings({"unchecked", "ConstantConditions"})
|
||||
@Inject(method = "set", at = @At("HEAD"))
|
||||
public void setPre(int id, Identifier identifier, Object object, CallbackInfoReturnable info) {
|
||||
int indexedEntriesId = indexedEntries.getId((T) object);
|
||||
if (indexedEntriesId >= 0) {
|
||||
throw new RuntimeException("Attempted to register object " + object + " twice! (at raw IDs " + indexedEntriesId + " and " + id + " )");
|
||||
}
|
||||
|
||||
if (!entries.containsKey(identifier)) {
|
||||
fabric_isObjectNew = true;
|
||||
} else {
|
||||
T oldObject = entries.get(identifier);
|
||||
int oldId = indexedEntries.getId(oldObject);
|
||||
if (oldObject != object || oldId != id) {
|
||||
if (oldObject != null && oldObject != object) {
|
||||
int oldId = indexedEntries.getId(oldObject);
|
||||
if (oldId != id) {
|
||||
throw new RuntimeException("Attempted to register ID " + identifier + " at different raw IDs (" + oldId + ", " + id + ")! If you're trying to override an item, use .set(), not .register()!");
|
||||
}
|
||||
|
||||
fabric_removeObjectEvent.invoker().onEntryRemoved(oldId, identifier, oldObject);
|
||||
fabric_isObjectNew = true;
|
||||
} else {
|
||||
|
@ -148,14 +156,18 @@ public abstract class MixinIdRegistry<T> implements RemappableRegistry, Listenab
|
|||
case AUTHORITATIVE:
|
||||
break;
|
||||
case REMOTE: {
|
||||
List<String> strings = new ArrayList<>();
|
||||
List<String> strings = null;
|
||||
for (Identifier remoteId : remoteIndexedEntries.keySet()) {
|
||||
if (!registry.getIds().contains(remoteId)) {
|
||||
if (!entries.keySet().contains(remoteId)) {
|
||||
if (strings == null) {
|
||||
strings = new ArrayList<>();
|
||||
}
|
||||
|
||||
strings.add(" - " + remoteId);
|
||||
}
|
||||
}
|
||||
|
||||
if (!strings.isEmpty()) {
|
||||
if (strings != null) {
|
||||
StringBuilder builder = new StringBuilder("Received ID map for " + name + " contains IDs unknown to the receiver!");
|
||||
for (String s : strings) {
|
||||
builder.append('\n').append(s);
|
||||
|
@ -164,10 +176,10 @@ public abstract class MixinIdRegistry<T> implements RemappableRegistry, Listenab
|
|||
}
|
||||
} break;
|
||||
case EXACT: {
|
||||
if (!registry.getIds().equals(remoteIndexedEntries.keySet())) {
|
||||
if (!entries.keySet().equals(remoteIndexedEntries.keySet())) {
|
||||
List<String> strings = new ArrayList<>();
|
||||
for (Identifier remoteId : remoteIndexedEntries.keySet()) {
|
||||
if (!registry.getIds().contains(remoteId)) {
|
||||
if (!entries.keySet().contains(remoteId)) {
|
||||
strings.add(" - " + remoteId + " (missing on local)");
|
||||
}
|
||||
}
|
||||
|
@ -196,53 +208,68 @@ public abstract class MixinIdRegistry<T> implements RemappableRegistry, Listenab
|
|||
if (fabric_prevIndexedEntries == null) {
|
||||
fabric_prevIndexedEntries = new Object2IntOpenHashMap<>();
|
||||
fabric_prevEntries = HashBiMap.create(entries);
|
||||
for (Identifier id : registry.getIds()) {
|
||||
//noinspection unchecked
|
||||
fabric_prevIndexedEntries.put(id, registry.getRawId(registry.get(id)));
|
||||
for (Object o : registry) {
|
||||
fabric_prevIndexedEntries.put(registry.getId(o), registry.getRawId(o));
|
||||
}
|
||||
}
|
||||
|
||||
Int2ObjectMap<Identifier> oldIdMap = new Int2ObjectOpenHashMap<>();
|
||||
for (Object o : registry) {
|
||||
oldIdMap.put(registry.getRawId(o), registry.getId(o));
|
||||
}
|
||||
|
||||
// If we're AUTHORITATIVE, we append entries which only exist on the
|
||||
// local side to the new entry list. For REMOTE, we instead drop them.
|
||||
if (mode == RemapMode.AUTHORITATIVE) {
|
||||
int maxValue = 0;
|
||||
switch (mode) {
|
||||
case AUTHORITATIVE: {
|
||||
int maxValue = 0;
|
||||
|
||||
Object2IntMap<Identifier> oldRemoteIndexedEntries = remoteIndexedEntries;
|
||||
remoteIndexedEntries = new Object2IntOpenHashMap<>();
|
||||
for (Identifier id : oldRemoteIndexedEntries.keySet()) {
|
||||
int v = oldRemoteIndexedEntries.getInt(id);
|
||||
remoteIndexedEntries.put(id, v);
|
||||
if (v > maxValue) maxValue = v;
|
||||
}
|
||||
|
||||
for (Identifier id : registry.getIds()) {
|
||||
if (!remoteIndexedEntries.containsKey(id)) {
|
||||
FABRIC_LOGGER.warn("Adding " + id + " to saved/remote registry.");
|
||||
remoteIndexedEntries.put(id, ++maxValue);
|
||||
Object2IntMap<Identifier> oldRemoteIndexedEntries = remoteIndexedEntries;
|
||||
remoteIndexedEntries = new Object2IntOpenHashMap<>();
|
||||
for (Identifier id : oldRemoteIndexedEntries.keySet()) {
|
||||
int v = oldRemoteIndexedEntries.getInt(id);
|
||||
remoteIndexedEntries.put(id, v);
|
||||
if (v > maxValue) maxValue = v;
|
||||
}
|
||||
}
|
||||
} else if (mode == RemapMode.REMOTE) {
|
||||
// TODO: Is this what mods really want?
|
||||
Set<Identifier> droppedIds = new HashSet<>();
|
||||
|
||||
for (Identifier id : registry.getIds()) {
|
||||
if (!remoteIndexedEntries.containsKey(id)) {
|
||||
droppedIds.add(id);
|
||||
Object object = registry.get(id);
|
||||
|
||||
// Emit RemoveObject events for removed objects.
|
||||
//noinspection unchecked
|
||||
fabric_getRemoveObjectEvent().invoker().onEntryRemoved(registry.getRawId(object), id, (T) object);
|
||||
for (Identifier id : registry.getIds()) {
|
||||
if (!remoteIndexedEntries.containsKey(id)) {
|
||||
FABRIC_LOGGER.warn("Adding " + id + " to saved/remote registry.");
|
||||
remoteIndexedEntries.put(id, ++maxValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case REMOTE: {
|
||||
// TODO: Is this what mods really want?
|
||||
Set<Identifier> droppedIds = new HashSet<>();
|
||||
|
||||
entries.keySet().removeAll(droppedIds);
|
||||
for (Identifier id : registry.getIds()) {
|
||||
if (!remoteIndexedEntries.containsKey(id)) {
|
||||
Object object = registry.get(id);
|
||||
int rid = registry.getRawId(object);
|
||||
|
||||
droppedIds.add(id);
|
||||
|
||||
// Emit RemoveObject events for removed objects.
|
||||
//noinspection unchecked
|
||||
fabric_getRemoveObjectEvent().invoker().onEntryRemoved(rid, id, (T) object);
|
||||
}
|
||||
}
|
||||
|
||||
// note: indexedEntries cannot be safely remove()d from
|
||||
entries.keySet().removeAll(droppedIds);
|
||||
} break;
|
||||
}
|
||||
|
||||
Int2IntMap idMap = new Int2IntOpenHashMap();
|
||||
for (Object o : indexedEntries) {
|
||||
Identifier id = registry.getId(o);
|
||||
idMap.put(registry.getRawId(o), remoteIndexedEntries.getInt(id));
|
||||
int rid = registry.getRawId(o);
|
||||
|
||||
// see above note
|
||||
if (remoteIndexedEntries.containsKey(id)) {
|
||||
idMap.put(rid, remoteIndexedEntries.getInt(id));
|
||||
}
|
||||
}
|
||||
|
||||
// entries was handled above, if it was necessary.
|
||||
|
@ -276,7 +303,7 @@ public abstract class MixinIdRegistry<T> implements RemappableRegistry, Listenab
|
|||
}
|
||||
|
||||
//noinspection unchecked
|
||||
fabric_getRemapEvent().invoker().onRemap(new RemapStateImpl(registry, idMap));
|
||||
fabric_getRemapEvent().invoker().onRemap(new RemapStateImpl(registry, oldIdMap, idMap));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -30,12 +30,10 @@ import org.spongepowered.asm.mixin.Shadow;
|
|||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.*;
|
||||
|
||||
@Mixin(WorldSaveHandler.class)
|
||||
public class MixinWorldSaveHandler {
|
||||
|
@ -49,6 +47,7 @@ public class MixinWorldSaveHandler {
|
|||
@Unique
|
||||
private CompoundTag fabric_lastSavedIdMap = null;
|
||||
|
||||
@Unique
|
||||
private boolean fabric_readIdMapFile(File file) throws IOException, RemapException {
|
||||
if (file.exists()) {
|
||||
FileInputStream fileInputStream = new FileInputStream(file);
|
||||
|
@ -63,20 +62,69 @@ public class MixinWorldSaveHandler {
|
|||
return false;
|
||||
}
|
||||
|
||||
private File getWorldIdMapFile(int i) {
|
||||
@Unique
|
||||
private File fabric_getWorldIdMapFile(int i) {
|
||||
return new File(new File(worldDir, "data"), "fabricRegistry" + ".dat" + (i == 0 ? "" : ("." + i)));
|
||||
}
|
||||
|
||||
@Unique
|
||||
private void fabric_saveRegistryData() {
|
||||
CompoundTag newIdMap = RegistrySyncManager.toTag(false);
|
||||
if (!newIdMap.equals(fabric_lastSavedIdMap)) {
|
||||
for (int i = FABRIC_ID_REGISTRY_BACKUPS - 1; i >= 0; i--) {
|
||||
File file = fabric_getWorldIdMapFile(i);
|
||||
if (file.exists()) {
|
||||
if (i == FABRIC_ID_REGISTRY_BACKUPS - 1) {
|
||||
file.delete();
|
||||
} else {
|
||||
File target = fabric_getWorldIdMapFile(i + 1);
|
||||
file.renameTo(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
File file = fabric_getWorldIdMapFile(0);
|
||||
File parentFile = file.getParentFile();
|
||||
if (!parentFile.exists()) {
|
||||
if (!parentFile.mkdirs()) {
|
||||
FABRIC_LOGGER.warn("[fabric-registry-sync] Could not create directory " + parentFile + "!");
|
||||
}
|
||||
}
|
||||
|
||||
FileOutputStream fileOutputStream = new FileOutputStream(file);
|
||||
NbtIo.writeCompressed(newIdMap, fileOutputStream);
|
||||
fileOutputStream.close();
|
||||
} catch (IOException e) {
|
||||
FABRIC_LOGGER.warn("[fabric-registry-sync] Failed to save registry file!", e);
|
||||
}
|
||||
|
||||
fabric_lastSavedIdMap = newIdMap;
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "saveWorld", at = @At("HEAD"))
|
||||
public void saveWorld(LevelProperties levelProperties, CompoundTag compoundTag, CallbackInfo info) {
|
||||
if (!worldDir.exists()) {
|
||||
return;
|
||||
}
|
||||
|
||||
fabric_saveRegistryData();
|
||||
}
|
||||
|
||||
// TODO: stop double save on client?
|
||||
@Inject(method = "readProperties", at = @At("HEAD"))
|
||||
public void readWorldProperties(CallbackInfoReturnable<LevelProperties> callbackInfo) {
|
||||
// Load
|
||||
for (int i = 0; i < FABRIC_ID_REGISTRY_BACKUPS; i++) {
|
||||
FABRIC_LOGGER.info("Loading Fabric registry [file " + (i + 1) + "/" + (FABRIC_ID_REGISTRY_BACKUPS + 1) + "]");
|
||||
FABRIC_LOGGER.trace("[fabric-registry-sync] Loading Fabric registry [file " + (i + 1) + "/" + (FABRIC_ID_REGISTRY_BACKUPS + 1) + "]");
|
||||
try {
|
||||
if (fabric_readIdMapFile(getWorldIdMapFile(i))) {
|
||||
break;
|
||||
if (fabric_readIdMapFile(fabric_getWorldIdMapFile(i))) {
|
||||
FABRIC_LOGGER.info("[fabric-registry-sync] Loaded registry data [file " + (i + 1) + "/" + (FABRIC_ID_REGISTRY_BACKUPS + 1) + "]");
|
||||
return;
|
||||
}
|
||||
} catch (FileNotFoundException e) {
|
||||
// pass
|
||||
} catch (IOException e) {
|
||||
if (i >= FABRIC_ID_REGISTRY_BACKUPS - 1) {
|
||||
throw new RuntimeException(e);
|
||||
|
@ -88,29 +136,7 @@ public class MixinWorldSaveHandler {
|
|||
}
|
||||
}
|
||||
|
||||
CompoundTag newIdMap = RegistrySyncManager.toTag(false);
|
||||
if (!newIdMap.equals(fabric_lastSavedIdMap)) {
|
||||
for (int i = FABRIC_ID_REGISTRY_BACKUPS - 1; i >= 0; i--) {
|
||||
File file = getWorldIdMapFile(i);
|
||||
if (file.exists()) {
|
||||
if (i == FABRIC_ID_REGISTRY_BACKUPS - 1) {
|
||||
file.delete();
|
||||
} else {
|
||||
File target = getWorldIdMapFile(i + 1);
|
||||
file.renameTo(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
FileOutputStream fileOutputStream = new FileOutputStream(getWorldIdMapFile(0));
|
||||
NbtIo.writeCompressed(newIdMap, fileOutputStream);
|
||||
fileOutputStream.close();
|
||||
} catch (IOException e) {
|
||||
FABRIC_LOGGER.warn("Failed to save registry file!", e);
|
||||
}
|
||||
|
||||
fabric_lastSavedIdMap = newIdMap;
|
||||
}
|
||||
// If not returned (not present), try saving the registry data
|
||||
fabric_saveRegistryData();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,6 +34,6 @@ public class MixinBlockColorMap {
|
|||
|
||||
@Inject(method = "<init>", at = @At("RETURN"))
|
||||
private void create(CallbackInfo info) {
|
||||
IdListTracker.register(Registry.BLOCK, providers);
|
||||
IdListTracker.register(Registry.BLOCK, "BlockColors.providers", providers);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,6 +34,6 @@ public class MixinItemColorMap {
|
|||
|
||||
@Inject(method = "<init>", at = @At("RETURN"))
|
||||
private void create(CallbackInfo info) {
|
||||
IdListTracker.register(Registry.ITEM, providers);
|
||||
IdListTracker.register(Registry.ITEM, "ItemColors.providers", providers);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@ public class MixinItemModelMap {
|
|||
|
||||
@Inject(method = "<init>", at = @At("RETURN"))
|
||||
public void onInit(BakedModelManager bakedModelManager, CallbackInfo info) {
|
||||
Int2ObjectMapTracker.register(Registry.ITEM, modelIds);
|
||||
Int2ObjectMapTracker.register(Registry.ITEM, models);
|
||||
Int2ObjectMapTracker.register(Registry.ITEM, "ItemModels.modelIds", modelIds);
|
||||
Int2ObjectMapTracker.register(Registry.ITEM, "ItemModels.models", models);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,6 +36,6 @@ public class MixinParticleManager {
|
|||
|
||||
@Inject(method = "<init>", at = @At("RETURN"))
|
||||
public void onInit(World world, TextureManager textureManager, CallbackInfo info) {
|
||||
Int2ObjectMapTracker.register(Registry.PARTICLE_TYPE, factories);
|
||||
Int2ObjectMapTracker.register(Registry.PARTICLE_TYPE, "ParticleManager.factories", factories);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
archivesBaseName = "fabric-renderer-indigo"
|
||||
version = getSubprojectVersion(project, "0.1.4")
|
||||
version = getSubprojectVersion(project, "0.1.6")
|
||||
|
||||
dependencies {
|
||||
compile project(path: ':fabric-api-base', configuration: 'dev')
|
||||
|
|
|
@ -35,6 +35,11 @@ import java.util.Properties;
|
|||
public class Indigo implements ClientModInitializer {
|
||||
public static final boolean ALWAYS_TESSELATE_INDIGO;
|
||||
public static final AoConfig AMBIENT_OCCLUSION_MODE;
|
||||
/** Set true in dev env to confirm results match vanilla when they should */
|
||||
public static final boolean DEBUG_COMPARE_LIGHTING;
|
||||
public static final boolean FIX_SMOOTH_LIGHTING_OFFSET;
|
||||
public static final boolean FIX_EXTERIOR_VERTEX_LIGHTING;
|
||||
|
||||
private static final Logger LOGGER = LogManager.getLogger();
|
||||
|
||||
private static boolean asBoolean(String property, boolean defValue) {
|
||||
|
@ -98,7 +103,10 @@ public class Indigo implements ClientModInitializer {
|
|||
}
|
||||
|
||||
ALWAYS_TESSELATE_INDIGO = asBoolean((String) properties.computeIfAbsent("always-tesselate-blocks", (a) -> "auto"), true);
|
||||
AMBIENT_OCCLUSION_MODE = asEnum((String) properties.computeIfAbsent("ambient-occlusion-mode", (a) -> "enhanced"), AoConfig.ENHANCED);
|
||||
AMBIENT_OCCLUSION_MODE = asEnum((String) properties.computeIfAbsent("ambient-occlusion-mode", (a) -> "hybrid"), AoConfig.HYBRID);
|
||||
DEBUG_COMPARE_LIGHTING = asBoolean((String) properties.computeIfAbsent("debug-compare-lighting", (a) -> "auto"), false);
|
||||
FIX_SMOOTH_LIGHTING_OFFSET = asBoolean((String) properties.computeIfAbsent("fix-smooth-lighting-offset", (a) -> "auto"), true);
|
||||
FIX_EXTERIOR_VERTEX_LIGHTING = asBoolean((String) properties.computeIfAbsent("fix-exterior-vertex-lighting", (a) -> "auto"), true);
|
||||
|
||||
try (FileOutputStream stream = new FileOutputStream(configFile)) {
|
||||
properties.store(stream, "Indigo properties file");
|
||||
|
|
|
@ -41,7 +41,7 @@ public abstract class RenderMaterialImpl {
|
|||
private static final int TEXTURE_DEPTH_MASK = 3;
|
||||
private static final int TEXTURE_DEPTH_SHIFT = 0;
|
||||
|
||||
private static final int BLEND_MODE_MASK = 3;
|
||||
private static final int BLEND_MODE_MASK = 7;
|
||||
private static final int[] BLEND_MODE_SHIFT = new int[3];
|
||||
private static final int[] COLOR_DISABLE_FLAGS = new int[3];
|
||||
private static final int[] EMISSIVE_FLAGS = new int[3];
|
||||
|
|
|
@ -74,8 +74,6 @@ public class AoCalculator {
|
|||
}
|
||||
|
||||
private static final Logger LOGGER = LogManager.getLogger();
|
||||
// TODO: make this actually configurable?
|
||||
private static final boolean fixSmoothLighting = true;
|
||||
|
||||
private final VanillaAoCalc vanillaCalc;
|
||||
private final BlockPos.Mutable lightPos = new BlockPos.Mutable();
|
||||
|
@ -112,12 +110,8 @@ public class AoCalculator {
|
|||
completionFlags = 0;
|
||||
}
|
||||
|
||||
/** Set true in dev env to confirm results match vanilla when they should */
|
||||
private static final boolean DEBUG = Boolean.valueOf(System.getProperty("fabric.debugAoLighting", "false"));
|
||||
|
||||
public void compute(MutableQuadViewImpl quad, boolean isVanilla) {
|
||||
final AoConfig config = Indigo.AMBIENT_OCCLUSION_MODE;
|
||||
|
||||
boolean shouldMatch = false;
|
||||
|
||||
switch(config) {
|
||||
|
@ -127,7 +121,7 @@ public class AoCalculator {
|
|||
|
||||
case EMULATE:
|
||||
calcFastVanilla(quad);
|
||||
shouldMatch = DEBUG && isVanilla;
|
||||
shouldMatch = Indigo.DEBUG_COMPARE_LIGHTING && isVanilla;
|
||||
break;
|
||||
|
||||
case HYBRID:
|
||||
|
@ -164,7 +158,7 @@ public class AoCalculator {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void calcVanilla(MutableQuadViewImpl quad) {
|
||||
vanillaCalc.compute(blockInfo, quad, ao, light);
|
||||
}
|
||||
|
@ -173,28 +167,15 @@ public class AoCalculator {
|
|||
int flags = quad.geometryFlags();
|
||||
|
||||
// force to block face if shape is full cube - matches vanilla logic
|
||||
if(((flags & LIGHT_FACE_FLAG) == 0) && Block.isShapeFullCube(blockInfo.blockState.getCollisionShape(blockInfo.blockView, blockInfo.blockPos))) {
|
||||
if((flags & LIGHT_FACE_FLAG) == 0 && (flags & AXIS_ALIGNED_FLAG) == AXIS_ALIGNED_FLAG && Block.isShapeFullCube(blockInfo.blockState.getCollisionShape(blockInfo.blockView, blockInfo.blockPos))) {
|
||||
flags |= LIGHT_FACE_FLAG;
|
||||
}
|
||||
|
||||
switch(flags) {
|
||||
case AXIS_ALIGNED_FLAG | CUBIC_FLAG | LIGHT_FACE_FLAG:
|
||||
vanillaFullFace(quad, true);
|
||||
break;
|
||||
|
||||
case AXIS_ALIGNED_FLAG | LIGHT_FACE_FLAG:
|
||||
vanillaPartialFace(quad, true);
|
||||
break;
|
||||
|
||||
case AXIS_ALIGNED_FLAG | CUBIC_FLAG:
|
||||
vanillaFullFace(quad, false);
|
||||
break;
|
||||
|
||||
default:
|
||||
case AXIS_ALIGNED_FLAG:
|
||||
vanillaPartialFace(quad, false);
|
||||
break;
|
||||
}
|
||||
|
||||
if((flags & CUBIC_FLAG) == 0) {
|
||||
vanillaPartialFace(quad, (flags & LIGHT_FACE_FLAG) != 0);
|
||||
} else {
|
||||
vanillaFullFace(quad, (flags & LIGHT_FACE_FLAG) != 0);
|
||||
}
|
||||
}
|
||||
|
||||
/** returns true if should match vanilla results */
|
||||
|
@ -202,11 +183,11 @@ public class AoCalculator {
|
|||
switch(quad.geometryFlags()) {
|
||||
case AXIS_ALIGNED_FLAG | CUBIC_FLAG | LIGHT_FACE_FLAG:
|
||||
vanillaFullFace(quad, true);
|
||||
return DEBUG;
|
||||
return Indigo.DEBUG_COMPARE_LIGHTING;
|
||||
|
||||
case AXIS_ALIGNED_FLAG | LIGHT_FACE_FLAG:
|
||||
vanillaPartialFace(quad, true);
|
||||
return DEBUG;
|
||||
return Indigo.DEBUG_COMPARE_LIGHTING;
|
||||
|
||||
case AXIS_ALIGNED_FLAG | CUBIC_FLAG:
|
||||
blendedFullFace(quad);
|
||||
|
@ -222,12 +203,12 @@ public class AoCalculator {
|
|||
}
|
||||
}
|
||||
|
||||
private void vanillaFullFace(MutableQuadViewImpl quad, boolean isOnLightFace) {
|
||||
private void vanillaFullFace(QuadViewImpl quad, boolean isOnLightFace) {
|
||||
final Direction lightFace = quad.lightFace();
|
||||
computeFace(lightFace, isOnLightFace).toArray(ao, light, VERTEX_MAP[lightFace.getId()]);
|
||||
}
|
||||
|
||||
private void vanillaPartialFace(MutableQuadViewImpl quad, boolean isOnLightFace) {
|
||||
private void vanillaPartialFace(QuadViewImpl quad, boolean isOnLightFace) {
|
||||
final Direction lightFace = quad.lightFace();
|
||||
AoFaceData faceData = computeFace(lightFace, isOnLightFace);
|
||||
final WeightFunction wFunc = AoFace.get(lightFace).weightFunc;
|
||||
|
@ -239,7 +220,7 @@ public class AoCalculator {
|
|||
}
|
||||
}
|
||||
|
||||
/** used in {@link #blendedInsetFace(VertexEditorImpl, Direction)} as return variable to avoid new allocation */
|
||||
/** used in {@link #blendedInsetFace(QuadViewImpl quad, int vertexIndex, Direction lightFace)} as return variable to avoid new allocation */
|
||||
AoFaceData tmpFace = new AoFaceData();
|
||||
|
||||
/** Returns linearly interpolated blend of outer and inner face based on depth of vertex in face */
|
||||
|
@ -250,7 +231,7 @@ public class AoCalculator {
|
|||
}
|
||||
|
||||
/**
|
||||
* Like {@link #blendedInsetFace(VertexEditorImpl, Direction)} but optimizes if depth is 0 or 1.
|
||||
* Like {@link #blendedInsetFace(QuadViewImpl quad, int vertexIndex, Direction lightFace)} but optimizes if depth is 0 or 1.
|
||||
* Used for irregular faces when depth varies by vertex to avoid unneeded interpolation.
|
||||
*/
|
||||
private AoFaceData gatherInsetFace(QuadViewImpl quad, int vertexIndex, Direction lightFace) {
|
||||
|
@ -265,12 +246,12 @@ public class AoCalculator {
|
|||
}
|
||||
}
|
||||
|
||||
private void blendedFullFace(MutableQuadViewImpl quad) {
|
||||
private void blendedFullFace(QuadViewImpl quad) {
|
||||
final Direction lightFace = quad.lightFace();
|
||||
blendedInsetFace(quad, 0, lightFace).toArray(ao, light, VERTEX_MAP[lightFace.getId()]);
|
||||
}
|
||||
|
||||
private void blendedPartialFace(MutableQuadViewImpl quad) {
|
||||
private void blendedPartialFace(QuadViewImpl quad) {
|
||||
final Direction lightFace = quad.lightFace();
|
||||
AoFaceData faceData = blendedInsetFace(quad, 0, lightFace);
|
||||
final WeightFunction wFunc = AoFace.get(lightFace).weightFunc;
|
||||
|
@ -383,16 +364,16 @@ public class AoCalculator {
|
|||
// vanilla was further offsetting these in the direction of the light face
|
||||
// but it was actually mis-sampling and causing visible artifacts in certain situation
|
||||
searchPos.set(lightPos).setOffset(aoFace.neighbors[0]);//.setOffset(lightFace);
|
||||
if(!fixSmoothLighting) searchPos.setOffset(lightFace);
|
||||
if(!Indigo.FIX_SMOOTH_LIGHTING_OFFSET) searchPos.setOffset(lightFace);
|
||||
final boolean isClear0 = world.getBlockState(searchPos).getLightSubtracted(world, searchPos) == 0;
|
||||
searchPos.set(lightPos).setOffset(aoFace.neighbors[1]);//.setOffset(lightFace);
|
||||
if(!fixSmoothLighting) searchPos.setOffset(lightFace);
|
||||
if(!Indigo.FIX_SMOOTH_LIGHTING_OFFSET) searchPos.setOffset(lightFace);
|
||||
final boolean isClear1 = world.getBlockState(searchPos).getLightSubtracted(world, searchPos) == 0;
|
||||
searchPos.set(lightPos).setOffset(aoFace.neighbors[2]);//.setOffset(lightFace);
|
||||
if(!fixSmoothLighting) searchPos.setOffset(lightFace);
|
||||
if(!Indigo.FIX_SMOOTH_LIGHTING_OFFSET) searchPos.setOffset(lightFace);
|
||||
final boolean isClear2 = world.getBlockState(searchPos).getLightSubtracted(world, searchPos) == 0;
|
||||
searchPos.set(lightPos).setOffset(aoFace.neighbors[3]);//.setOffset(lightFace);
|
||||
if(!fixSmoothLighting) searchPos.setOffset(lightFace);
|
||||
if(!Indigo.FIX_SMOOTH_LIGHTING_OFFSET) searchPos.setOffset(lightFace);
|
||||
final boolean isClear3 = world.getBlockState(searchPos).getLightSubtracted(world, searchPos) == 0;
|
||||
|
||||
// c = corner - values at corners of face
|
||||
|
@ -469,7 +450,7 @@ public class AoCalculator {
|
|||
* value from all four samples.
|
||||
*/
|
||||
private static int meanBrightness(int a, int b, int c, int d) {
|
||||
if(fixSmoothLighting) {
|
||||
if(Indigo.FIX_SMOOTH_LIGHTING_OFFSET) {
|
||||
return a == 0 || b == 0 || c == 0 || d == 0 ? meanEdgeBrightness(a, b, c, d) : meanInnerBrightness(a, b, c, d);
|
||||
} else {
|
||||
return vanillaMeanBrightness(a, b, c, d);
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
package net.fabricmc.indigo.renderer.aocalc;
|
||||
|
||||
import static net.minecraft.util.math.Direction.*;
|
||||
|
||||
import static net.fabricmc.indigo.renderer.aocalc.AoVertexClampFunction.CLAMP_FUNC;
|
||||
import net.fabricmc.api.EnvType;
|
||||
import net.fabricmc.api.Environment;
|
||||
import net.minecraft.util.SystemUtil;
|
||||
|
@ -30,55 +30,55 @@ import net.fabricmc.indigo.renderer.mesh.QuadViewImpl;
|
|||
*/
|
||||
@Environment(EnvType.CLIENT)
|
||||
enum AoFace {
|
||||
AOF_DOWN(new Direction[]{WEST, EAST, NORTH, SOUTH}, (q, i) -> q.y(i),
|
||||
AOF_DOWN(new Direction[]{WEST, EAST, NORTH, SOUTH}, (q, i) -> CLAMP_FUNC.clamp(q.y(i)),
|
||||
(q, i, w) -> {
|
||||
final float u = q.x(i);
|
||||
final float v = q.z(i);
|
||||
final float u = CLAMP_FUNC.clamp(q.x(i));
|
||||
final float v = CLAMP_FUNC.clamp(q.z(i));
|
||||
w[0] = (1-u) * v;
|
||||
w[1] = (1-u) * (1-v);
|
||||
w[2] = u * (1-v);
|
||||
w[3] = u * v;
|
||||
}),
|
||||
AOF_UP(new Direction[]{EAST, WEST, NORTH, SOUTH}, (q, i) -> 1 - q.y(i),
|
||||
AOF_UP(new Direction[]{EAST, WEST, NORTH, SOUTH}, (q, i) -> 1 - CLAMP_FUNC.clamp(q.y(i)),
|
||||
(q, i, w) -> {
|
||||
final float u = q.x(i);
|
||||
final float v = q.z(i);
|
||||
final float u = CLAMP_FUNC.clamp(q.x(i));
|
||||
final float v = CLAMP_FUNC.clamp(q.z(i));
|
||||
w[0] = u * v;
|
||||
w[1] = u * (1-v);
|
||||
w[2] = (1-u) * (1-v);
|
||||
w[3] = (1-u) * v;
|
||||
}),
|
||||
AOF_NORTH(new Direction[]{UP, DOWN, EAST, WEST}, (q, i) -> q.z(i),
|
||||
AOF_NORTH(new Direction[]{UP, DOWN, EAST, WEST}, (q, i) -> CLAMP_FUNC.clamp(q.z(i)),
|
||||
(q, i, w) -> {
|
||||
final float u = q.y(i);
|
||||
final float v = q.x(i);
|
||||
final float u = CLAMP_FUNC.clamp(q.y(i));
|
||||
final float v = CLAMP_FUNC.clamp(q.x(i));
|
||||
w[0] = u * (1-v);
|
||||
w[1] = u * v;
|
||||
w[2] = (1-u) * v;
|
||||
w[3] = (1-u) * (1-v);
|
||||
}),
|
||||
AOF_SOUTH(new Direction[]{WEST, EAST, DOWN, UP}, (q, i) -> 1 - q.z(i),
|
||||
AOF_SOUTH(new Direction[]{WEST, EAST, DOWN, UP}, (q, i) -> 1 - CLAMP_FUNC.clamp(q.z(i)),
|
||||
(q, i, w) -> {
|
||||
final float u = q.y(i);
|
||||
final float v = q.x(i);
|
||||
final float u = CLAMP_FUNC.clamp(q.y(i));
|
||||
final float v = CLAMP_FUNC.clamp(q.x(i));
|
||||
w[0] = u * (1-v);
|
||||
w[1] = (1-u) * (1-v);
|
||||
w[2] = (1-u) * v;
|
||||
w[3] = u * v;
|
||||
}),
|
||||
AOF_WEST(new Direction[]{UP, DOWN, NORTH, SOUTH}, (q, i) -> q.x(i),
|
||||
AOF_WEST(new Direction[]{UP, DOWN, NORTH, SOUTH}, (q, i) -> CLAMP_FUNC.clamp(q.x(i)),
|
||||
(q, i, w) -> {
|
||||
final float u = q.y(i);
|
||||
final float v = q.z(i);
|
||||
final float u = CLAMP_FUNC.clamp(q.y(i));
|
||||
final float v = CLAMP_FUNC.clamp(q.z(i));
|
||||
w[0] = u * v;
|
||||
w[1] = u * (1-v);
|
||||
w[2] = (1-u) * (1-v);
|
||||
w[3] = (1-u) * v;
|
||||
}),
|
||||
AOF_EAST(new Direction[]{DOWN, UP, NORTH, SOUTH}, (q, i) -> 1 - q.x(i),
|
||||
AOF_EAST(new Direction[]{DOWN, UP, NORTH, SOUTH}, (q, i) -> 1 - CLAMP_FUNC.clamp(q.x(i)),
|
||||
(q, i, w) -> {
|
||||
final float u = q.y(i);
|
||||
final float v = q.z(i);
|
||||
final float u = CLAMP_FUNC.clamp(q.y(i));
|
||||
final float v = CLAMP_FUNC.clamp(q.z(i));
|
||||
w[0] = (1-u) * v;
|
||||
w[1] = (1-u) * (1-v);
|
||||
w[2] = u * (1-v);
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 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.indigo.renderer.aocalc;
|
||||
|
||||
import net.fabricmc.api.EnvType;
|
||||
import net.fabricmc.api.Environment;
|
||||
import net.fabricmc.indigo.Indigo;
|
||||
|
||||
@Environment(EnvType.CLIENT)
|
||||
@FunctionalInterface
|
||||
interface AoVertexClampFunction {
|
||||
float clamp(float x);
|
||||
|
||||
AoVertexClampFunction CLAMP_FUNC = Indigo.FIX_EXTERIOR_VERTEX_LIGHTING ? x -> x < 0f ? 0f : (x > 1f ? 1f : x) : x -> x;
|
||||
}
|
|
@ -39,7 +39,10 @@ public abstract class GeometryHelper {
|
|||
|
||||
/** set when a quad is coplanar with its light face. Implies {@link #AXIS_ALIGNED_FLAG} */
|
||||
public static final int LIGHT_FACE_FLAG = AXIS_ALIGNED_FLAG << 1;
|
||||
|
||||
|
||||
private static final float EPS_MIN = 0.0001f;
|
||||
private static final float EPS_MAX = 1.0f - EPS_MIN;
|
||||
|
||||
private GeometryHelper() {}
|
||||
|
||||
/**
|
||||
|
@ -56,10 +59,10 @@ public abstract class GeometryHelper {
|
|||
if(isParallelQuadOnFace(lightFace, quad)) {
|
||||
bits |= LIGHT_FACE_FLAG;
|
||||
}
|
||||
if(isQuadCubic(lightFace, quad)) {
|
||||
bits |= CUBIC_FLAG;
|
||||
}
|
||||
}
|
||||
if(isQuadCubic(lightFace, quad)) {
|
||||
bits |= CUBIC_FLAG;
|
||||
}
|
||||
return bits;
|
||||
}
|
||||
|
||||
|
@ -80,17 +83,17 @@ public abstract class GeometryHelper {
|
|||
}
|
||||
|
||||
/**
|
||||
* True if quad - already known to be parallel to a face - is actually coplanar with it.<p>
|
||||
* True if quad - already known to be parallel to a face - is actually coplanar with it.
|
||||
* For compatibility with vanilla resource packs, also true if quad is outside the face.<p>
|
||||
*
|
||||
* Test will be unreliable if not already parallel, use {@link #isQuadParallel(Direction, QuadView)}
|
||||
* Test will be unreliable if not already parallel, use {@link #isQuadParallelToFace(Direction, QuadView)}
|
||||
* for that purpose. Expects convex quads with all points co-planar.<p>
|
||||
*/
|
||||
public static boolean isParallelQuadOnFace(Direction lightFace, QuadView quad) {
|
||||
if(lightFace == null)
|
||||
return false;
|
||||
final int coordinateIndex = lightFace.getAxis().ordinal();
|
||||
final float expectedValue = lightFace.getDirection() == AxisDirection.POSITIVE ? 1 : 0;
|
||||
return equalsApproximate(quad.posByIndex(0, coordinateIndex), expectedValue);
|
||||
final float x = quad.posByIndex(0, lightFace.getAxis().ordinal());
|
||||
return lightFace.getDirection() == AxisDirection.POSITIVE ? x >= EPS_MAX : x <= EPS_MIN;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -101,7 +104,7 @@ public abstract class GeometryHelper {
|
|||
* quad vertices are coplanar with each other. <p>
|
||||
*
|
||||
* Expects convex quads with all points co-planar.<p>
|
||||
*
|
||||
*
|
||||
* @param lightFace MUST be non-null.
|
||||
*/
|
||||
public static boolean isQuadCubic(Direction lightFace, QuadView quad) {
|
||||
|
@ -134,10 +137,13 @@ public abstract class GeometryHelper {
|
|||
|
||||
return confirmSquareCorners(a, b, quad);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Used by {@link #isQuadCubic(Direction, int[], int, QuadSerializer)}.
|
||||
* True if quad touches all four corners of unit square.
|
||||
* Used by {@link #isQuadCubic(Direction, QuadView)}.
|
||||
* True if quad touches all four corners of unit square.<p>
|
||||
*
|
||||
* For compatibility with resource packs that contain models with quads exceeding
|
||||
* block boundaries, considers corners outside the block to be at the corners.
|
||||
*/
|
||||
private static boolean confirmSquareCorners(int aCoordinate, int bCoordinate, QuadView quad) {
|
||||
int flags = 0;
|
||||
|
@ -146,18 +152,18 @@ public abstract class GeometryHelper {
|
|||
final float a = quad.posByIndex(i, aCoordinate);
|
||||
final float b = quad.posByIndex(i, bCoordinate);
|
||||
|
||||
if(equalsApproximate(a, 0)) {
|
||||
if(equalsApproximate(b, 0)) {
|
||||
if(a <= EPS_MIN) {
|
||||
if(b <= EPS_MIN) {
|
||||
flags |= 1;
|
||||
} else if(equalsApproximate(b, 1)) {
|
||||
} else if(b >= EPS_MAX) {
|
||||
flags |= 2;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else if(equalsApproximate(a, 1)) {
|
||||
if(equalsApproximate(b, 0)) {
|
||||
} else if(a >= EPS_MAX) {
|
||||
if(b <= EPS_MIN) {
|
||||
flags |= 4;
|
||||
} else if(equalsApproximate(b, 1)) {
|
||||
} else if(b >= EPS_MAX) {
|
||||
flags |= 8;
|
||||
} else {
|
||||
return false;
|
||||
|
|
|
@ -27,6 +27,7 @@ import net.fabricmc.indigo.renderer.aocalc.AoCalculator;
|
|||
import net.fabricmc.indigo.renderer.helper.ColorHelper;
|
||||
import net.fabricmc.indigo.renderer.mesh.EncodingFormat;
|
||||
import net.fabricmc.indigo.renderer.mesh.MutableQuadViewImpl;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
|
||||
|
@ -140,7 +141,7 @@ public abstract class AbstractQuadRenderer {
|
|||
*/
|
||||
int flatBrightness(MutableQuadViewImpl quad, BlockState blockState, BlockPos pos) {
|
||||
mpos.set(pos);
|
||||
if((quad.geometryFlags() & LIGHT_FACE_FLAG) != 0) {
|
||||
if((quad.geometryFlags() & LIGHT_FACE_FLAG) != 0 || Block.isShapeFullCube(blockState.getCollisionShape(blockInfo.blockView, pos))) {
|
||||
mpos.setOffset(quad.lightFace());
|
||||
}
|
||||
return brightnessFunc.applyAsInt(blockState, mpos);
|
||||
|
|
|
@ -132,7 +132,7 @@ public class TerrainFallbackConsumer extends AbstractQuadRenderer implements Con
|
|||
// vanilla compatibility hack
|
||||
// For flat lighting, cull face drives everything and light face is ignored.
|
||||
if(cullFace == null) {
|
||||
editorQuad.geometryFlags(0);
|
||||
editorQuad.invalidateShape();
|
||||
} else {
|
||||
editorQuad.geometryFlags(GeometryHelper.LIGHT_FACE_FLAG);
|
||||
editorQuad.lightFace(cullFace);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
archivesBaseName = "fabric-rendering-v0"
|
||||
version = getSubprojectVersion(project, "0.1.0")
|
||||
version = getSubprojectVersion(project, "0.1.1")
|
||||
|
||||
dependencies {
|
||||
compile project(path: ':fabric-api-base', configuration: 'dev')
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 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.api.client.render;
|
||||
|
||||
import net.fabricmc.fabric.api.event.Event;
|
||||
import net.fabricmc.fabric.api.event.EventFactory;
|
||||
|
||||
/**
|
||||
* Called when the world renderer reloads, usually as result of changing resource pack
|
||||
* or video configuration, or when the player types F3+A in the debug screen.
|
||||
* Afterwards all render chunks will be reset and reloaded.<p>
|
||||
*
|
||||
* Render chunks and other render-related object instances will be made null
|
||||
* or invalid after this event so do not use it to capture dependent state.
|
||||
* Instead, use it to invalidate state and reinitialize lazily.
|
||||
*/
|
||||
public interface InvalidateRenderStateCallback {
|
||||
public static final Event<InvalidateRenderStateCallback> EVENT = EventFactory.createArrayBacked(InvalidateRenderStateCallback.class,
|
||||
(listeners) -> () -> {
|
||||
for (InvalidateRenderStateCallback event : listeners) {
|
||||
event.onInvalidate();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
void onInvalidate();
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 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.mixin.client.render;
|
||||
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import net.fabricmc.fabric.api.client.render.InvalidateRenderStateCallback;
|
||||
import net.minecraft.client.render.WorldRenderer;
|
||||
|
||||
@Mixin(WorldRenderer.class)
|
||||
public abstract class MixinWorldRenderer {
|
||||
@Inject(method = "reload", at = @At("HEAD"))
|
||||
private void onReload(CallbackInfo ci) {
|
||||
InvalidateRenderStateCallback.EVENT.invoker().onInvalidate();
|
||||
}
|
||||
}
|
|
@ -6,7 +6,8 @@
|
|||
"MixinBlockColorMap",
|
||||
"MixinBlockEntityRenderManager",
|
||||
"MixinEntityRenderManager",
|
||||
"MixinItemColorMap"
|
||||
"MixinItemColorMap",
|
||||
"MixinWorldRenderer"
|
||||
],
|
||||
"injectors": {
|
||||
"defaultRequire": 1
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
archivesBaseName = "fabric-tag-extensions-v0"
|
||||
version = getSubprojectVersion(project, "0.1.0")
|
||||
version = getSubprojectVersion(project, "0.1.1")
|
||||
|
||||
dependencies {
|
||||
compile project(path: ':fabric-api-base', configuration: 'dev')
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 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.api.tag;
|
||||
|
||||
/**
|
||||
* Interface implemented by {@link net.minecraft.tag.Tag} instances when
|
||||
* Fabric API is present.
|
||||
*
|
||||
* @param <T>
|
||||
*/
|
||||
public interface FabricTag<T> {
|
||||
/**
|
||||
* @return True if the given tag has been "replaced" by a datapack at least once.
|
||||
*/
|
||||
boolean hasBeenReplaced();
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 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.api.tag;
|
||||
|
||||
/**
|
||||
* Interface implemented by {@link net.minecraft.tag.Tag.Builder} instances when
|
||||
* Fabric API is present.
|
||||
*
|
||||
* @param <T>
|
||||
*/
|
||||
public interface FabricTagBuilder<T> {
|
||||
/**
|
||||
* Clear the contained entries and mark the tag as replaced.
|
||||
*/
|
||||
void clearTagEntries();
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 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.tag;
|
||||
|
||||
public interface FabricTagHooks {
|
||||
void fabric_setExtraData(int clearCount);
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 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.mixin.tag;
|
||||
|
||||
import net.fabricmc.fabric.api.tag.FabricTag;
|
||||
import net.fabricmc.fabric.impl.tag.FabricTagHooks;
|
||||
import net.minecraft.tag.Tag;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
|
||||
@Mixin(Tag.class)
|
||||
public class MixinTag<T> implements FabricTag<T>, FabricTagHooks {
|
||||
@Unique
|
||||
private int fabric_clearCount;
|
||||
|
||||
@Override
|
||||
public boolean hasBeenReplaced() {
|
||||
return fabric_clearCount > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fabric_setExtraData(int clearCount) {
|
||||
this.fabric_clearCount = clearCount;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 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.mixin.tag;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.gson.JsonObject;
|
||||
import net.fabricmc.fabric.api.tag.FabricTagBuilder;
|
||||
import net.fabricmc.fabric.impl.tag.FabricTagHooks;
|
||||
import net.minecraft.tag.Tag;
|
||||
import net.minecraft.util.Identifier;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
@Mixin(Tag.Builder.class)
|
||||
public class MixinTagBuilder<T> implements FabricTagBuilder<T> {
|
||||
@Shadow
|
||||
private Set<Tag.Entry<T>> entries;
|
||||
|
||||
@Unique
|
||||
private int fabric_clearCount;
|
||||
|
||||
@Inject(at = @At("RETURN"), method = "build")
|
||||
public void onBuildFinished(Identifier id, CallbackInfoReturnable<Tag<T>> info) {
|
||||
((FabricTagHooks) info.getReturnValue()).fabric_setExtraData(fabric_clearCount);
|
||||
}
|
||||
|
||||
@Inject(at = @At(value = "INVOKE", target = "Ljava/util/Set;clear()V"), method = "fromJson")
|
||||
public void onFromJsonClear(Function<Identifier, T> function_1, JsonObject jsonObject_1, CallbackInfoReturnable<Tag.Builder<T>> info) {
|
||||
fabric_clearCount++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearTagEntries() {
|
||||
entries.clear();
|
||||
fabric_clearCount++;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"required": true,
|
||||
"package": "net.fabricmc.fabric.mixin.tag",
|
||||
"compatibilityLevel": "JAVA_8",
|
||||
"mixins": [
|
||||
"MixinTag",
|
||||
"MixinTagBuilder"
|
||||
],
|
||||
"injectors": {
|
||||
"defaultRequire": 1
|
||||
}
|
||||
}
|
|
@ -7,5 +7,8 @@
|
|||
"fabricloader": ">=0.4.0",
|
||||
"fabric-api-base": "*",
|
||||
"fabric-resource-loader-v0": "*"
|
||||
}
|
||||
},
|
||||
"mixins": [
|
||||
"fabric-tag-extensions-v0.mixins.json"
|
||||
]
|
||||
}
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* 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.loot;
|
||||
|
||||
import net.fabricmc.api.ModInitializer;
|
||||
import net.fabricmc.fabric.api.event.loot.LootTableLoadingCallback;
|
||||
import net.fabricmc.fabric.api.loot.FabricLootPoolBuilder;
|
||||
import net.fabricmc.fabric.api.loot.LootUtilities;
|
||||
import net.minecraft.item.Items;
|
||||
import net.minecraft.world.loot.ConstantLootTableRange;
|
||||
import net.minecraft.world.loot.LootPool;
|
||||
import net.minecraft.world.loot.condition.SurvivesExplosionLootCondition;
|
||||
import net.minecraft.world.loot.entry.ItemEntry;
|
||||
import net.minecraft.world.loot.entry.LootEntry;
|
||||
|
||||
public class LootTableMod implements ModInitializer {
|
||||
private static final String LOOT_ENTRY_JSON = "{\"type\":\"minecraft:item\",\"name\":\"minecraft:apple\"}";
|
||||
|
||||
@Override
|
||||
public void onInitialize() {
|
||||
LootTableLoadingCallback.EVENT.register((manager, id, supplier) -> {
|
||||
if ("minecraft:blocks/dirt".equals(id.toString())) {
|
||||
LootEntry entryFromString = LootUtilities.readEntryFromJson(LOOT_ENTRY_JSON);
|
||||
|
||||
LootPool pool = FabricLootPoolBuilder.builder()
|
||||
.withEntry(ItemEntry.builder(Items.FEATHER))
|
||||
.withEntry(entryFromString)
|
||||
.withRolls(ConstantLootTableRange.create(1))
|
||||
.withCondition(SurvivesExplosionLootCondition.method_871())
|
||||
.build();
|
||||
|
||||
supplier.withPool(pool);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -22,6 +22,7 @@ include 'fabric-events-interaction-v0'
|
|||
include 'fabric-events-lifecycle-v0'
|
||||
include 'fabric-item-groups-v0'
|
||||
include 'fabric-keybindings-v0'
|
||||
include 'fabric-loot-tables-v1'
|
||||
include 'fabric-mining-levels-v0'
|
||||
include 'fabric-models-v0'
|
||||
include 'fabric-networking-v0'
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue