fix stronghold deadlock

This commit is contained in:
dfsek
2022-05-31 18:33:39 -07:00
parent 68ac277471
commit 10308959a1
4 changed files with 186 additions and 79 deletions

View File

@@ -42,28 +42,15 @@ public class NMSBiomeInjector {
private static final Logger LOGGER = LoggerFactory.getLogger(NMSBiomeInjector.class);
private static final Map<MinecraftKey, List<MinecraftKey>> terraBiomeMap = new HashMap<>();
public static IRegistryWritable<BiomeBase> getBiomeRegistry() {
CraftServer craftserver = (CraftServer) Bukkit.getServer();
DedicatedServer dedicatedserver = craftserver.getServer();
return (IRegistryWritable<BiomeBase>) dedicatedserver
.aU() // getRegistryManager
.b( // getRegistry
IRegistry.aP // biome registry key
);
}
public static void registerBiomes(ConfigRegistry configRegistry) {
try {
LOGGER.info("Hacking biome registry...");
IRegistryWritable<BiomeBase> biomeRegistry = getBiomeRegistry();
IRegistryWritable<BiomeBase> biomeRegistry = (IRegistryWritable<BiomeBase>) Registries.biomeRegistry();
Field frozen = RegistryMaterials.class.getDeclaredField("bL"); // registry frozen field
frozen.setAccessible(true);
frozen.set(biomeRegistry, false);
configRegistry.forEach(pack -> pack.getRegistry(Biome.class).forEach((key, biome) -> {
try {
BukkitPlatformBiome platformBiome = (BukkitPlatformBiome) biome.getPlatformBiome();
@@ -90,53 +77,48 @@ public class NMSBiomeInjector {
frozen.set(biomeRegistry, true); // freeze registry again :)
LOGGER.info("Doing tag garbage....");
Map<TagKey<BiomeBase>, List<Holder<BiomeBase>>> collect = biomeRegistry
.g() // streamKeysAndEntries
.collect(HashMap::new,
(map, pair) ->
map.put(pair.getFirst(), new ArrayList<>(pair.getSecond().a().toList())),
HashMap::putAll);
terraBiomeMap
.forEach((vb, terraBiomes) ->
getEntry(biomeRegistry, vb)
.ifPresentOrElse(
vanilla -> terraBiomes
.forEach(tb -> getEntry(biomeRegistry, tb)
.ifPresentOrElse(
terra -> {
LOGGER.debug(vanilla.e().orElseThrow().a() +
" (vanilla for " +
terra.e().orElseThrow().a() +
": " +
vanilla.c().toList());
vanilla.c()
.forEach(
tag -> collect
.computeIfAbsent(tag,
t -> new ArrayList<>())
.add(terra));
},
() -> LOGGER.error(
"No such biome: {}",
tb))),
() -> LOGGER.error("No vanilla biome: {}", vb)));
biomeRegistry.k(); // clearTags
biomeRegistry.a(ImmutableMap.copyOf(collect)); // populateTags
} catch(NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException exception) {
throw new RuntimeException(exception);
}
}
public static void injectTags() {
LOGGER.info("Doing tag garbage....");
IRegistryWritable<BiomeBase> biomeRegistry = getBiomeRegistry();
Map<TagKey<BiomeBase>, List<Holder<BiomeBase>>> collect = biomeRegistry
.g() // streamKeysAndEntries
.collect(HashMap::new,
(map, pair) ->
map.put(pair.getFirst(), new ArrayList<>(pair.getSecond().a().toList())),
HashMap::putAll);
terraBiomeMap
.forEach((vb, terraBiomes) ->
getEntry(biomeRegistry, vb)
.ifPresentOrElse(
vanilla -> terraBiomes
.forEach(tb -> getEntry(biomeRegistry, tb)
.ifPresentOrElse(
terra -> {
LOGGER.debug(vanilla.e().orElseThrow().a() +
" (vanilla for " +
terra.e().orElseThrow().a() +
": " +
vanilla.c().toList());
vanilla.c()
.forEach(
tag -> collect
.computeIfAbsent(tag,
t -> new ArrayList<>())
.add(terra));
},
() -> LOGGER.error(
"No such biome: {}",
tb))),
() -> LOGGER.error("No vanilla biome: {}", vb)));
biomeRegistry.k(); // clearTags
biomeRegistry.a(ImmutableMap.copyOf(collect)); // populateTags
}
public static <T> Optional<Holder<T>> getEntry(IRegistry<T> registry, MinecraftKey identifier) {
return registry.b(identifier)
.flatMap(registry::c)

View File

@@ -1,13 +1,20 @@
package com.dfsek.terra.bukkit.nms;
import com.mojang.datafixers.util.Pair;
import com.mojang.serialization.Codec;
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap;
import net.minecraft.core.BlockPosition;
import net.minecraft.core.Holder;
import net.minecraft.core.IRegistry;
import net.minecraft.core.IRegistryCustom;
import net.minecraft.core.SectionPosition;
import net.minecraft.server.dedicated.DedicatedServer;
import net.minecraft.server.level.RegionLimitedWorldAccess;
import net.minecraft.world.level.BlockColumn;
import net.minecraft.world.level.ChunkCoordIntPair;
import net.minecraft.world.level.LevelHeightAccessor;
import net.minecraft.world.level.StructureManager;
import net.minecraft.world.level.biome.BiomeBase;
import net.minecraft.world.level.biome.BiomeManager;
import net.minecraft.world.level.biome.Climate;
import net.minecraft.world.level.biome.Climate.Sampler;
@@ -19,21 +26,35 @@ import net.minecraft.world.level.levelgen.HeightMap;
import net.minecraft.world.level.levelgen.WorldGenStage;
import net.minecraft.world.level.levelgen.blending.Blender;
import net.minecraft.world.level.levelgen.structure.StructureSet;
import net.minecraft.world.level.levelgen.structure.placement.ConcentricRingsStructurePlacement;
import net.minecraft.world.level.levelgen.structure.placement.StructurePlacement;
import net.minecraft.world.level.levelgen.structure.templatesystem.DefinedStructureManager;
import org.bukkit.Bukkit;
import org.bukkit.craftbukkit.v1_18_R2.CraftServer;
import org.bukkit.craftbukkit.v1_18_R2.block.data.CraftBlockData;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.stream.Collectors;
import com.dfsek.terra.api.config.ConfigPack;
import com.dfsek.terra.api.util.generic.Lazy;
import com.dfsek.terra.api.world.biome.generation.BiomeProvider;
import com.dfsek.terra.api.world.info.WorldProperties;
public class NMSChunkGeneratorDelegate extends ChunkGenerator {
private static final Logger LOGGER = LoggerFactory.getLogger(NMSChunkGeneratorDelegate.class);
private final NMSBiomeProvider biomeSource;
private final com.dfsek.terra.api.world.chunk.generation.ChunkGenerator delegate;
@@ -42,20 +63,12 @@ public class NMSChunkGeneratorDelegate extends ChunkGenerator {
private final long seed;
public static IRegistry<StructureSet> getStructureRegistry() {
CraftServer craftserver = (CraftServer) Bukkit.getServer();
DedicatedServer dedicatedserver = craftserver.getServer();
return dedicatedserver
.aU() // getRegistryManager
.b( // getRegistry
IRegistry.aM // biome registry key
);
}
private final Map<ConcentricRingsStructurePlacement, Lazy<List<ChunkCoordIntPair>>> h = new Object2ObjectArrayMap<>();
public NMSChunkGeneratorDelegate(ChunkGenerator vanilla, ConfigPack pack, NMSBiomeProvider biomeProvider, long seed) {
super(getStructureRegistry(), Optional.empty(), biomeProvider, biomeProvider, seed);
super(Registries.structureSet(), Optional.empty(), biomeProvider, biomeProvider, seed);
this.delegate = pack.getGeneratorProvider().newInstance(pack);
this.vanilla = vanilla;
this.biomeSource = biomeProvider;
@@ -141,6 +154,91 @@ public class NMSChunkGeneratorDelegate extends ChunkGenerator {
return y;
}
@Override
public void a(IRegistryCustom iregistrycustom, StructureManager structuremanager, IChunkAccess ichunkaccess,
DefinedStructureManager definedstructuremanager, long i) {
super.a(iregistrycustom, structuremanager, ichunkaccess, definedstructuremanager, i);
}
@Nullable
@Override
public List<ChunkCoordIntPair> a(ConcentricRingsStructurePlacement concentricringsstructureplacement) {
this.i();
return this.h.get(concentricringsstructureplacement).value();
}
private volatile boolean rings = false;
@Override
public synchronized void i() {
if(!this.rings) {
super.i();
this.populateStrongholdData();
this.rings = true;
}
}
private void populateStrongholdData() {
LOGGER.info("Generating safe stronghold data. This may take up to a minute.");
Set<Holder<BiomeBase>> set = this.d.b();
a().map(h -> h.a()).forEach((holder) -> { // we dont need the spigot crap because it doesnt touch concentric.
StructurePlacement structureplacement = holder.b();
if(structureplacement instanceof ConcentricRingsStructurePlacement concentricringsstructureplacement) {
if(holder.a().stream().anyMatch((structureset_a1) -> structureset_a1.a(set::contains))) {
this.h.put(concentricringsstructureplacement,
Lazy.lazy(() -> this.generateRingPositions(holder, concentricringsstructureplacement)));
}
}
});
}
private List<ChunkCoordIntPair> generateRingPositions(StructureSet holder,
ConcentricRingsStructurePlacement concentricringsstructureplacement) {
if(concentricringsstructureplacement.d() == 0) {
return List.of();
}
List<ChunkCoordIntPair> list = new ArrayList<>();
Set<Holder<BiomeBase>> set = holder.a().stream().flatMap((structureset_a) -> (structureset_a.a().a()).a().a()).collect(
Collectors.toSet());
int i = concentricringsstructureplacement.b();
int j = concentricringsstructureplacement.d();
int k = concentricringsstructureplacement.c();
Random random = new Random();
random.setSeed(this.j);
double d0 = random.nextDouble() * Math.PI * 2.0;
int l = 0;
int i1 = 0;
for(int j1 = 0; j1 < j; ++j1) {
double d1 = (double) (4 * i + i * i1 * 6) + (random.nextDouble() - 0.5) * (double) i * 2.5;
int k1 = (int) Math.round(Math.cos(d0) * d1);
int l1 = (int) Math.round(Math.sin(d0) * d1);
int i2 = SectionPosition.a(k1, 8);
int j2 = SectionPosition.a(l1, 8);
Objects.requireNonNull(set);
Pair<BlockPosition, Holder<BiomeBase>> pair = this.c.a(i2, 0, j2, 112, set::contains, random, this.d());
if(pair != null) {
BlockPosition blockposition = pair.getFirst();
k1 = SectionPosition.a(blockposition.u());
l1 = SectionPosition.a(blockposition.w());
}
list.add(new ChunkCoordIntPair(k1, l1));
d0 += 6.283185307179586 / (double) k;
++l;
if(l == k) {
++i1;
l = 0;
k += 2 * k / (i1 + 1);
k = Math.min(k, j - j1);
d0 += random.nextDouble() * Math.PI * 2.0;
}
}
System.out.println(list);
return list;
}
public int getMinimumY() {
return h();
}

View File

@@ -1,5 +1,7 @@
package com.dfsek.terra.bukkit.nms;
import com.dfsek.terra.api.util.generic.Construct;
import net.minecraft.server.level.WorldServer;
import net.minecraft.world.level.chunk.ChunkGenerator;
import org.bukkit.World;
@@ -11,8 +13,11 @@ import org.bukkit.event.world.WorldLoadEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.Field;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.ReentrantLock;
import com.dfsek.terra.api.config.ConfigPack;
@@ -24,8 +29,6 @@ public class NMSInjectListener implements Listener {
private static final Set<World> INJECTED = new HashSet<>();
private static final ReentrantLock INJECT_LOCK = new ReentrantLock();
private volatile boolean tags = false;
@EventHandler
public void onWorldInit(WorldInitEvent event) {
if (!INJECTED.contains(event.getWorld()) && event.getWorld().getGenerator() instanceof BukkitChunkGeneratorWrapper bukkitChunkGeneratorWrapper) {
@@ -44,19 +47,12 @@ public class NMSInjectListener implements Listener {
custom.conf = vanilla.conf; // world config from Spigot
serverWorld.k().a.u = custom;
serverWorld.generator = null;
LOGGER.info("Successfully injected into world.");
serverWorld.k().a.u.i(); // generate stronghold data now
INJECT_LOCK.unlock();
}
}
@EventHandler
public void onWorldLoad(WorldLoadEvent load) {
if(!tags) {
System.out.println("Injecting tags late to prevent deadlock. Thank you Bukkit.");
tags = true;
NMSBiomeInjector.injectTags();
}
}
}

View File

@@ -0,0 +1,31 @@
package com.dfsek.terra.bukkit.nms;
import net.minecraft.core.IRegistry;
import net.minecraft.core.IRegistryWritable;
import net.minecraft.resources.ResourceKey;
import net.minecraft.server.dedicated.DedicatedServer;
import net.minecraft.world.level.biome.BiomeBase;
import net.minecraft.world.level.levelgen.structure.StructureSet;
import org.bukkit.Bukkit;
import org.bukkit.craftbukkit.v1_18_R2.CraftServer;
public class Registries {
private static <T> IRegistry<T> getRegistry(ResourceKey<IRegistry<T>> key) {
CraftServer craftserver = (CraftServer) Bukkit.getServer();
DedicatedServer dedicatedserver = craftserver.getServer();
return dedicatedserver
.aU() // getRegistryManager
.b( // getRegistry
key
);
}
public static IRegistry<BiomeBase> biomeRegistry() {
return getRegistry(IRegistry.aP);
}
public static IRegistry<StructureSet> structureSet() {
return getRegistry(IRegistry.aM);
}
}