Update to forge 39.0.59

This commit is contained in:
IzzelAliz 2022-01-26 21:22:18 +08:00
parent f681dd0456
commit 223df78a0e
No known key found for this signature in database
GPG Key ID: EE50E123A11D8338
13 changed files with 104 additions and 234 deletions

View File

@ -4,13 +4,13 @@ A Bukkit server implementation utilizing Mixin.
![Actions](https://img.shields.io/github/workflow/status/IzzelAliz/Arclight/Java%20CI%20with%20Gradle?style=flat-square) ![GitHub](https://img.shields.io/github/license/IzzelAliz/Arclight?style=flat-square) ![Actions](https://img.shields.io/github/workflow/status/IzzelAliz/Arclight/Java%20CI%20with%20Gradle?style=flat-square) ![GitHub](https://img.shields.io/github/license/IzzelAliz/Arclight?style=flat-square)
| Minecraft | Forge | Status | Build | | Minecraft | Forge | Status | Build |
| :----: | :----: | :---: | :---: | | :----: |:-------:| :---: | :---: |
| 1.18.x | 38.0.12 | ACTIVE | [![1.18 Status](https://img.shields.io/appveyor/build/IzzelAliz/arclight-18?style=flat-square)](https://ci.appveyor.com/project/IzzelAliz/arclight-18) | | 1.18.x | 39.0.59 | ACTIVE | [![1.18 Status](https://img.shields.io/appveyor/build/IzzelAliz/arclight-18?style=flat-square)](https://ci.appveyor.com/project/IzzelAliz/arclight-18) |
| 1.17.x | 37.1.0 | [LEGACY](https://github.com/IzzelAliz/Arclight/releases/tag/1.17/1.0.2) | [![1.17 Status](https://img.shields.io/appveyor/build/IzzelAliz/arclight-17?style=flat-square)](https://ci.appveyor.com/project/IzzelAliz/arclight-17) | | 1.17.x | 37.1.0 | [LEGACY](https://github.com/IzzelAliz/Arclight/releases/tag/1.17/1.0.2) | [![1.17 Status](https://img.shields.io/appveyor/build/IzzelAliz/arclight-17?style=flat-square)](https://ci.appveyor.com/project/IzzelAliz/arclight-17) |
| 1.16.x | 36.2.19 | ACTIVE | [![1.16 Status](https://img.shields.io/appveyor/build/IzzelAliz/arclight-16?style=flat-square)](https://ci.appveyor.com/project/IzzelAliz/arclight-16) | | 1.16.x | 36.2.19 | ACTIVE | [![1.16 Status](https://img.shields.io/appveyor/build/IzzelAliz/arclight-16?style=flat-square)](https://ci.appveyor.com/project/IzzelAliz/arclight-16) |
| 1.15.x | 31.2.48 | [LEGACY](https://github.com/IzzelAliz/Arclight/releases/tag/1.15/1.0.19) | [![1.15 Status](https://img.shields.io/appveyor/build/IzzelAliz/arclight-15?style=flat-square)](https://ci.appveyor.com/project/IzzelAliz/arclight-15) | | 1.15.x | 31.2.48 | [LEGACY](https://github.com/IzzelAliz/Arclight/releases/tag/1.15/1.0.19) | [![1.15 Status](https://img.shields.io/appveyor/build/IzzelAliz/arclight-15?style=flat-square)](https://ci.appveyor.com/project/IzzelAliz/arclight-15) |
| 1.14.x | 28.2.0 | [LEGACY](https://github.com/IzzelAliz/Arclight/releases/tag/1.0.6) | [![1.14 Status](https://img.shields.io/appveyor/build/IzzelAliz/arclight?style=flat-square)](https://ci.appveyor.com/project/IzzelAliz/arclight) | | 1.14.x | 28.2.0 | [LEGACY](https://github.com/IzzelAliz/Arclight/releases/tag/1.0.6) | [![1.14 Status](https://img.shields.io/appveyor/build/IzzelAliz/arclight?style=flat-square)](https://ci.appveyor.com/project/IzzelAliz/arclight) |
* Legacy version still accepts pull requests. * Legacy version still accepts pull requests.

View File

@ -1587,14 +1587,10 @@ public abstract class ServerPlayNetHandlerMixin implements ServerPlayNetHandlerB
ItemStack itemstack = packetplayinsetcreativeslot.getItem(); ItemStack itemstack = packetplayinsetcreativeslot.getItem();
final CompoundTag nbttagcompound = itemstack.getTagElement("BlockEntityTag"); final CompoundTag nbttagcompound = itemstack.getTagElement("BlockEntityTag");
if (!itemstack.isEmpty() && nbttagcompound != null && nbttagcompound.contains("x") && nbttagcompound.contains("y") && nbttagcompound.contains("z")) { if (!itemstack.isEmpty() && nbttagcompound != null && nbttagcompound.contains("x") && nbttagcompound.contains("y") && nbttagcompound.contains("z")) {
final BlockPos blockposition = new BlockPos(nbttagcompound.getInt("x"), nbttagcompound.getInt("y"), nbttagcompound.getInt("z")); BlockPos blockpos = BlockEntity.getPosFromTag(nbttagcompound);
final BlockEntity tileentity = this.player.level.getBlockEntity(blockposition); BlockEntity blockentity = this.player.level.getBlockEntity(blockpos);
if (tileentity != null) { if (blockentity != null) {
final CompoundTag nbttagcompound2 = tileentity.save(new CompoundTag()); blockentity.saveToItem(itemstack);
nbttagcompound2.remove("x");
nbttagcompound2.remove("y");
nbttagcompound2.remove("z");
itemstack.addTagElement("BlockEntityTag", nbttagcompound2);
} }
} }
final boolean flag2 = packetplayinsetcreativeslot.getSlotNum() >= 1 && packetplayinsetcreativeslot.getSlotNum() <= 45; final boolean flag2 = packetplayinsetcreativeslot.getSlotNum() >= 1 && packetplayinsetcreativeslot.getSlotNum() <= 45;

View File

@ -679,7 +679,20 @@ public abstract class LivingEntityMixin extends EntityMixin implements LivingEnt
float hardHatModifier = hardHat.apply((double) f).floatValue(); float hardHatModifier = hardHat.apply((double) f).floatValue();
f += hardHatModifier; f += hardHatModifier;
Function<Double, Double> blocking = f13 -> -((this.isDamageSourceBlocked(damagesource)) ? f13 : 0.0); Function<Double, Double> blocking;
var shieldTakesDamage = false;
if (this.isDamageSourceBlocked(damagesource)) {
var shieldEvent = ForgeHooks.onShieldBlock((LivingEntity) (Object) this, damagesource, f);
if (!shieldEvent.isCanceled()) {
var blocked = shieldEvent.getBlockedDamage();
shieldTakesDamage = shieldEvent.shieldTakesDamage();
blocking = f13 -> -(double) blocked;
} else {
blocking = f13 -> 0d;
}
} else {
blocking = f13 -> 0d;
}
float blockingModifier = blocking.apply((double) f).floatValue(); float blockingModifier = blocking.apply((double) f).floatValue();
f += blockingModifier; f += blockingModifier;
@ -743,7 +756,9 @@ public abstract class LivingEntityMixin extends EntityMixin implements LivingEnt
// Apply blocking code // PAIL: steal from above // Apply blocking code // PAIL: steal from above
if (event.getDamage(EntityDamageEvent.DamageModifier.BLOCKING) < 0) { if (event.getDamage(EntityDamageEvent.DamageModifier.BLOCKING) < 0) {
this.level.broadcastEntityEvent((Entity) (Object) this, (byte) 29); // SPIGOT-4635 - shield damage sound this.level.broadcastEntityEvent((Entity) (Object) this, (byte) 29); // SPIGOT-4635 - shield damage sound
this.hurtCurrentlyUsedShield((float) -event.getDamage(EntityDamageEvent.DamageModifier.BLOCKING)); if (shieldTakesDamage) {
this.hurtCurrentlyUsedShield((float) -event.getDamage(EntityDamageEvent.DamageModifier.BLOCKING));
}
Entity entity = damagesource.getDirectEntity(); Entity entity = damagesource.getDirectEntity();
if (entity instanceof LivingEntity) { if (entity instanceof LivingEntity) {

View File

@ -1,83 +0,0 @@
package io.izzel.arclight.common.mixin.forge;
import com.google.common.collect.Multimap;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.storage.LevelStorageSource;
import net.minecraft.world.level.storage.WorldData;
import net.minecraftforge.common.util.MavenVersionStringHelper;
import net.minecraftforge.fml.ModList;
import net.minecraftforge.registries.ForgeRegistry;
import net.minecraftforge.registries.GameData;
import net.minecraftforge.registries.RegistryManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.Marker;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.Map;
@Mixin(targets = "net.minecraftforge.common.ForgeMod$FMLWorldPersistenceHook", remap = false)
public class FMLWorldPersistenceHookMixin {
@Shadow @Final private static Logger LOGGER;
@Shadow @Final private static Marker WORLDPERSISTENCE;
private final Map<Path, CompoundTag> map = new HashMap<>();
private boolean injected = false;
@Redirect(method = "readData", at = @At(value = "INVOKE", target = "Lnet/minecraftforge/registries/GameData;injectSnapshot(Ljava/util/Map;ZZ)Lcom/google/common/collect/Multimap;"))
private Multimap<ResourceLocation, ResourceLocation> arclight$handleInject(Map<ResourceLocation, ForgeRegistry.Snapshot> snapshot, boolean injectFrozenData, boolean isLocalWorld,
LevelStorageSource.LevelStorageAccess levelSave, WorldData serverInfo, CompoundTag tag) {
if (!injected) {
injected = true;
return GameData.injectSnapshot(snapshot, injectFrozenData, isLocalWorld);
} else {
// TODO Properly remap registry and id
map.put(levelSave.getWorldDir(), tag.getCompound("Registries").copy());
LOGGER.debug(WORLDPERSISTENCE, "Skipped registry injection for {}", serverInfo.getLevelName());
return null;
}
}
/**
* @author IzzelAliz
* @reason
*/
@Overwrite
public CompoundTag getDataForWriting(LevelStorageSource.LevelStorageAccess levelSave, WorldData serverInfo) {
CompoundTag fmlData = new CompoundTag();
ListTag modList = new ListTag();
ModList.get().getMods().forEach(mi ->
{
final CompoundTag mod = new CompoundTag();
mod.putString("ModId", mi.getModId());
mod.putString("ModVersion", MavenVersionStringHelper.artifactVersionToString(mi.getVersion()));
modList.add(mod);
});
fmlData.put("LoadingModList", modList);
CompoundTag nbt = map.get(levelSave.getWorldDir());
if (nbt != null) {
fmlData.put("Registries", nbt);
LOGGER.debug(WORLDPERSISTENCE, "Skipped ID Map collection for {}", serverInfo.getLevelName());
} else {
CompoundTag registries = new CompoundTag();
fmlData.put("Registries", registries);
LOGGER.debug(WORLDPERSISTENCE, "Gathering id map for writing to world save {}", serverInfo.getLevelName());
for (Map.Entry<ResourceLocation, ForgeRegistry.Snapshot> e : RegistryManager.ACTIVE.takeSnapshot(true).entrySet()) {
registries.put(e.getKey().toString(), e.getValue().write());
}
LOGGER.debug(WORLDPERSISTENCE, "ID Map collection complete {}", serverInfo.getLevelName());
}
return fmlData;
}
}

View File

@ -0,0 +1,28 @@
package io.izzel.arclight.common.mixin.forge;
import io.izzel.arclight.common.mod.ArclightMod;
import io.izzel.arclight.common.mod.server.ArclightPermissionHandler;
import io.izzel.arclight.i18n.ArclightConfig;
import net.minecraftforge.server.permission.PermissionAPI;
import net.minecraftforge.server.permission.handler.IPermissionHandler;
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;
@Mixin(value = PermissionAPI.class, remap = false)
public class PermissionAPIMixin {
@Shadow private static IPermissionHandler activeHandler;
@Inject(method = "initializePermissionAPI", at = @At("RETURN"))
private static void arclight$init(CallbackInfo ci) {
if (!ArclightConfig.spec().getCompat().isForwardPermission()) {
return;
}
var handler = new ArclightPermissionHandler(activeHandler);
ArclightMod.LOGGER.info("Forwarding forge permission[{}] to bukkit", activeHandler.getIdentifier());
activeHandler = handler;
}
}

View File

@ -1,13 +1,11 @@
package io.izzel.arclight.common.mod; package io.izzel.arclight.common.mod;
import io.izzel.arclight.common.mod.server.ArclightPermissionHandler;
import io.izzel.arclight.common.mod.server.event.ArclightEventDispatcherRegistry; import io.izzel.arclight.common.mod.server.event.ArclightEventDispatcherRegistry;
import io.izzel.arclight.common.mod.util.log.ArclightI18nLogger; import io.izzel.arclight.common.mod.util.log.ArclightI18nLogger;
import net.minecraftforge.fml.IExtensionPoint; import net.minecraftforge.fml.IExtensionPoint;
import net.minecraftforge.fml.ModLoadingContext; import net.minecraftforge.fml.ModLoadingContext;
import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.network.NetworkConstants; import net.minecraftforge.network.NetworkConstants;
import net.minecraftforge.server.permission.PermissionAPI;
import org.apache.logging.log4j.Level; import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
@ -29,7 +27,6 @@ public class ArclightMod {
ArclightEventDispatcherRegistry.registerAllEventDispatchers(); ArclightEventDispatcherRegistry.registerAllEventDispatchers();
ModLoadingContext.get().registerExtensionPoint(IExtensionPoint.DisplayTest.class, ModLoadingContext.get().registerExtensionPoint(IExtensionPoint.DisplayTest.class,
() -> new IExtensionPoint.DisplayTest(() -> NetworkConstants.IGNORESERVERONLY, (a, b) -> true)); () -> new IExtensionPoint.DisplayTest(() -> NetworkConstants.IGNORESERVERONLY, (a, b) -> true));
PermissionAPI.setPermissionHandler(ArclightPermissionHandler.INSTANCE);
} }
private static class LoggingPrintStream extends PrintStream { private static class LoggingPrintStream extends PrintStream {

View File

@ -1,112 +1,54 @@
package io.izzel.arclight.common.mod.server; package io.izzel.arclight.common.mod.server;
import com.mojang.authlib.GameProfile; import io.izzel.arclight.common.bridge.core.entity.player.ServerPlayerEntityBridge;
import io.izzel.arclight.common.bridge.core.entity.player.PlayerEntityBridge; import net.minecraft.resources.ResourceLocation;
import net.minecraftforge.server.permission.DefaultPermissionLevel; import net.minecraft.server.level.ServerPlayer;
import net.minecraftforge.server.permission.IPermissionHandler; import net.minecraftforge.server.permission.handler.IPermissionHandler;
import net.minecraftforge.server.permission.context.IContext; import net.minecraftforge.server.permission.nodes.PermissionDynamicContext;
import net.minecraftforge.server.permission.nodes.PermissionNode;
import net.minecraftforge.server.permission.nodes.PermissionTypes;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.permissions.Permission;
import org.bukkit.permissions.PermissionDefault;
import org.bukkit.util.permissions.DefaultPermissions;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection; import java.util.Objects;
import java.util.LinkedList; import java.util.Set;
import java.util.List; import java.util.UUID;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;
public class ArclightPermissionHandler implements IPermissionHandler { public final class ArclightPermissionHandler implements IPermissionHandler {
public static final ArclightPermissionHandler INSTANCE = new ArclightPermissionHandler(); private final IPermissionHandler delegate;
private final List<Perm> list = new LinkedList<>(); public ArclightPermissionHandler(IPermissionHandler delegate) {
private final AtomicBoolean initialized = new AtomicBoolean(false); Objects.requireNonNull(delegate, "permission handler");
this.delegate = delegate;
}
public void initialize() { @Override
if (!initialized.getAndSet(true)) { public ResourceLocation getIdentifier() {
for (Perm permission : this.list) { return new ResourceLocation("arclight", "permission");
DefaultPermissions.registerPermission(permission.toBukkit()); }
}
this.list.clear(); @Override
public Set<PermissionNode<?>> getRegisteredNodes() {
return delegate.getRegisteredNodes();
}
@SuppressWarnings("unchecked")
@Override
public <T> T getPermission(ServerPlayer player, PermissionNode<T> node, PermissionDynamicContext<?>... context) {
if (node.getType() == PermissionTypes.BOOLEAN) {
return (T) (Object) ((ServerPlayerEntityBridge) player).bridge$getBukkitEntity().hasPermission(node.getNodeName());
} else {
return delegate.getPermission(player, node, context);
} }
} }
@Override @Override
public void registerNode(@NotNull String node, @NotNull DefaultPermissionLevel level, @NotNull String desc) { public <T> T getOfflinePermission(UUID uuid, PermissionNode<T> node, PermissionDynamicContext<?>... context) {
PermissionDefault bukkit; var player = Bukkit.getPlayer(uuid);
if (level == DefaultPermissionLevel.ALL) { if (player != null && node.getType() == PermissionTypes.BOOLEAN) {
bukkit = PermissionDefault.TRUE; return (T) (Object) player.hasPermission(node.getNodeName());
} else if (level == DefaultPermissionLevel.OP) {
bukkit = PermissionDefault.OP;
} else { } else {
bukkit = PermissionDefault.FALSE; return delegate.getOfflinePermission(uuid, node, context);
}
if (initialized.get()) {
DefaultPermissions.registerPermission(node, desc, bukkit);
} else {
this.list.add(new Perm(node, desc, bukkit));
}
}
@Override
public @NotNull Collection<String> getRegisteredNodes() {
if (initialized.get()) {
return Bukkit.getPluginManager().getPermissions().stream().map(Permission::getName).collect(Collectors.toList());
} else {
return this.list.stream().map(it -> it.node).collect(Collectors.toList());
}
}
@Override
public boolean hasPermission(@NotNull GameProfile profile, @NotNull String node, @Nullable IContext context) {
if (context != null) {
net.minecraft.world.entity.player.Player player = context.getPlayer();
if (player != null) {
return ((PlayerEntityBridge) player).bridge$getBukkitEntity().hasPermission(node);
}
}
Player player = Bukkit.getPlayer(profile.getId());
if (player != null) {
return player.hasPermission(node);
} else {
Permission perm = Bukkit.getServer().getPluginManager().getPermission(node);
boolean isOp = ArclightServer.getMinecraftServer().getPlayerList().isOp(profile);
if (perm != null) {
return perm.getDefault().getValue(isOp);
} else {
return Permission.DEFAULT_PERMISSION.getValue(isOp);
}
}
}
@Override
public @NotNull String getNodeDescription(@NotNull String node) {
if (!initialized.get()) {
return "";
} else {
Permission permission = Bukkit.getPluginManager().getPermission(node);
return permission == null ? "" : permission.getDescription();
}
}
private static class Perm {
private final String node;
private final String desc;
private final PermissionDefault level;
public Perm(String node, String desc, PermissionDefault level) {
this.node = node;
this.desc = desc;
this.level = level;
}
public Permission toBukkit() {
return new Permission(node, desc, level);
} }
} }
} }

View File

@ -33,7 +33,7 @@ public class ArclightServer {
server = new CraftServer(console, playerList); server = new CraftServer(console, playerList);
((MinecraftServerBridge) console).bridge$setServer(server); ((MinecraftServerBridge) console).bridge$setServer(server);
((MinecraftServerBridge) console).bridge$setConsole(ColouredConsoleSender.getInstance()); ((MinecraftServerBridge) console).bridge$setConsole(ColouredConsoleSender.getInstance());
ArclightPermissionHandler.INSTANCE.initialize();
} catch (Throwable t) { } catch (Throwable t) {
throw new RuntimeException("Error initializing Arclight", t); throw new RuntimeException("Error initializing Arclight", t);
} }

View File

@ -10,10 +10,10 @@
}, },
"compatibilityLevel": "JAVA_11", "compatibilityLevel": "JAVA_11",
"mixins": [ "mixins": [
"FMLWorldPersistenceHookMixin",
"ForgeEventFactoryMixin", "ForgeEventFactoryMixin",
"ForgeHooksMixin", "ForgeHooksMixin",
"NetworkHooksMixin", "NetworkHooksMixin",
"PacketDistributorMixin" "PacketDistributorMixin",
"PermissionAPIMixin"
] ]
} }

View File

@ -5,11 +5,6 @@ import io.izzel.arclight.api.Unsafe;
import io.izzel.arclight.boot.AbstractBootstrap; import io.izzel.arclight.boot.AbstractBootstrap;
import io.izzel.arclight.i18n.ArclightConfig; import io.izzel.arclight.i18n.ArclightConfig;
import io.izzel.arclight.i18n.ArclightLocale; import io.izzel.arclight.i18n.ArclightLocale;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import java.util.ServiceLoader; import java.util.ServiceLoader;
import java.util.function.Consumer; import java.util.function.Consumer;
@ -41,7 +36,6 @@ public class ApplicationBootstrap extends AbstractBootstrap implements Consumer<
try { try {
this.setupMod(); this.setupMod();
this.dirtyHacks(); this.dirtyHacks();
this.hackModlauncher();
ServiceLoader.load(getClass().getModule().getLayer(), Consumer.class).stream() ServiceLoader.load(getClass().getModule().getLayer(), Consumer.class).stream()
.filter(it -> !it.type().getName().contains("arclight")) .filter(it -> !it.type().getName().contains("arclight"))
.findFirst().orElseThrow().get().accept(args); .findFirst().orElseThrow().get().accept(args);
@ -50,31 +44,4 @@ public class ApplicationBootstrap extends AbstractBootstrap implements Consumer<
System.err.println("Fail to launch Arclight."); System.err.println("Fail to launch Arclight.");
} }
} }
private void hackModlauncher() throws Exception {
try (var in = getClass().getClassLoader().getResourceAsStream("cpw/mods/modlauncher/TransformerClassWriter$SuperCollectingVisitor.class")) {
var cw = new ClassWriter(0);
var cr = new ClassReader(in);
cr.accept(new ClassVisitor(Opcodes.ASM9, cw) {
@Override
public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
var mv = super.visitMethod(access, name, descriptor, signature, exceptions);
if (name.equals("<init>")) {
return new MethodVisitor(Opcodes.ASM9, mv) {
@Override
public void visitLdcInsn(Object value) {
if (value.equals(Opcodes.ASM7)) {
super.visitLdcInsn(Opcodes.ASM9);
} else {
super.visitLdcInsn(value);
}
}
};
} else return mv;
}
}, 0);
var bytes = cw.toByteArray();
Unsafe.defineClass(cr.getClassName(), bytes, 0, bytes.length, getClass().getClassLoader(), getClass().getProtectionDomain());
}
}
} }

View File

@ -14,7 +14,7 @@ allprojects {
ext { ext {
agpVersion = '1.22' agpVersion = '1.22'
minecraftVersion = '1.18.1' minecraftVersion = '1.18.1'
forgeVersion = '39.0.0' forgeVersion = '39.0.59'
apiVersion = '1.2.5' apiVersion = '1.2.5'
toolsVersion = '1.3.+' toolsVersion = '1.3.+'
mixinVersion = '0.8.5' mixinVersion = '0.8.5'

View File

@ -22,6 +22,9 @@ public class CompatSpec {
@Setting("extra-logic-worlds") @Setting("extra-logic-worlds")
private List<String> extraLogicWorlds; private List<String> extraLogicWorlds;
@Setting("forward-permission")
private boolean forwardPermission;
public Map<String, MaterialPropertySpec> getMaterials() { public Map<String, MaterialPropertySpec> getMaterials() {
return materials; return materials;
} }
@ -45,4 +48,8 @@ public class CompatSpec {
public List<String> getExtraLogicWorlds() { public List<String> getExtraLogicWorlds() {
return extraLogicWorlds; return extraLogicWorlds;
} }
public boolean isForwardPermission() {
return forwardPermission;
}
} }

View File

@ -17,6 +17,7 @@ compatibility {
extra-logic-worlds = [ extra-logic-worlds = [
"com.example.mod.ExtraLogicWorld" "com.example.mod.ExtraLogicWorld"
] ]
forward-permission = true
} }
async-catcher { async-catcher {
dump = true dump = true