Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
package net.meatwo310.softdeepslate;

import net.meatwo310.softdeepslate.config.ModServerConfig;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockState;

import java.util.HashSet;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;

public final class SoftDeepslateLogic {
private final ModServerConfig config;
private final BlockResolver blockResolver;
private volatile Set<Block> blocksCache;

public SoftDeepslateLogic(ModServerConfig config, BlockResolver blockResolver) {
this.config = Objects.requireNonNull(config);
this.blockResolver = Objects.requireNonNull(blockResolver);
}

public boolean shouldMineFaster(BlockState state) {
return getBlocks().contains(state.getBlock());
}

public double miningSpeed() {
return config.miningSpeed();
}

public void invalidateBlockCache() {
blocksCache = null;
}

private Set<Block> getBlocks() {
Set<Block> cached = blocksCache;
if (cached == null) {
synchronized (this) {
cached = blocksCache;
if (cached == null) {
cached = buildCache();
blocksCache = cached;
}
}
}
return cached;
}

private Set<Block> buildCache() {
HashSet<Block> blocks = new HashSet<>();

for (String name : config.blocks()) {
if (name.startsWith("#")) {
cacheBlockTag(blocks, name.substring(1));
} else {
cacheBlock(blocks, name);
}
}

if (blocks.isEmpty()) {
Constants.LOGGER.warn("No valid blocks found");
} else {
Constants.LOGGER.info("Cached {} blocks", blocks.size());
}

return Set.copyOf(blocks);
}

private void cacheBlock(Set<Block> blocks, String blockName) {
ResourceLocation id = ResourceLocation.tryParse(blockName);
if (id == null) {
return;
}

blockResolver.resolveBlock(id).ifPresentOrElse(
block -> {
Constants.LOGGER.debug("Caching block: {}{}", blockName, blocks.contains(block) ? " (DUPLICATED)" : "");
blocks.add(block);
},
() -> Constants.LOGGER.warn("Unknown block in config: {}", blockName)
);
}

private void cacheBlockTag(Set<Block> blocks, String blockTagName) {
ResourceLocation id = ResourceLocation.tryParse(blockTagName);
if (id == null) {
return;
}

blockResolver.resolveTag(id).ifPresentOrElse(
taggedBlocks -> {
for (Block block : taggedBlocks) {
Constants.LOGGER.debug(
"Caching block from tag #{}: {}{}",
blockTagName,
blockResolver.blockName(block),
blocks.contains(block) ? " (DUPLICATED)" : ""
);
blocks.add(block);
}
},
() -> Constants.LOGGER.warn("Unknown block tag in config: #{}", blockTagName)
);
}

public interface BlockResolver {
Optional<Block> resolveBlock(ResourceLocation id);
Optional<? extends Iterable<Block>> resolveTag(ResourceLocation id);
String blockName(Block block);
}
}
111 changes: 37 additions & 74 deletions 1.20.1-forge/src/main/java/net/meatwo310/softdeepslate/ModMain.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@
import net.minecraft.resources.ResourceLocation;
import net.minecraft.tags.TagKey;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraftforge.common.util.Lazy;
import net.minecraftforge.event.TagsUpdatedEvent;
import net.minecraftforge.event.entity.player.PlayerEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
Expand All @@ -16,103 +14,68 @@
import net.minecraftforge.registries.tags.ITag;
import net.minecraftforge.registries.tags.ITagManager;

import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;

@Mod(Constants.MODID)
public class ModMain {
private static Lazy<Set<Block>> blocksCache = Lazy.of(ModMain::buildCache);
private static SoftDeepslateLogic logic;

public ModMain(FMLJavaModLoadingContext ctx) {
Constants.LOGGER.debug(Constants.INITIALIZING, ModUtils.loc("1.20.1-forge"));
logic = new SoftDeepslateLogic(ServerConfig.INSTANCE, new ForgeBlockResolver());
ctx.registerConfig(ModConfig.Type.SERVER, ServerConfig.SPEC);
}

private static Set<Block> buildCache() {
HashSet<Block> blocks = new HashSet<>();

for (String name : ServerConfig.BLOCKS.get()) {
if (name.startsWith("#")) {
cacheBlockTag(blocks, name.substring(1));
} else {
cacheBlock(blocks, name);
@Mod.EventBusSubscriber(modid = Constants.MODID)
public static class Subscriber {
@SubscribeEvent
public static void onPlayerBreakSpeed(PlayerEvent.BreakSpeed event) {
SoftDeepslateLogic logic = logic();
if (logic.shouldMineFaster(event.getState())) {
event.setNewSpeed((float) (event.getNewSpeed() * logic.miningSpeed()));
}
}

if (blocks.isEmpty()) {
Constants.LOGGER.warn("No valid blocks found");
} else {
Constants.LOGGER.info("Cached {} blocks", blocks.size());
@SubscribeEvent
public static void onTagsUpdated(TagsUpdatedEvent event) {
logic().invalidateBlockCache();
}

return Set.copyOf(blocks);
}

private static void cacheBlock(Set<Block> blocks, String blockName) {
ResourceLocation id = ResourceLocation.tryParse(blockName);
if (id == null) {
return;
private static SoftDeepslateLogic logic() {
if (logic == null) {
throw new IllegalStateException("SoftDeepslateLogic has not been initialized");
}

Optional.ofNullable(ForgeRegistries.BLOCKS.getValue(id)).ifPresentOrElse(
block -> {
Constants.LOGGER.debug("Caching block: {}{}", blockName, blocks.contains(block) ? " (DUPLICATED)" : "");
blocks.add(block);
},
() -> Constants.LOGGER.warn("Unknown block in config: {}", blockName)
);
return logic;
}

private static void cacheBlockTag(Set<Block> blocks, String blockTagName) {
ResourceLocation id = ResourceLocation.tryParse(blockTagName);
if (id == null) {
return;
}

ITagManager<Block> tagManager = ForgeRegistries.BLOCKS.tags();
if (tagManager == null) {
Constants.LOGGER.warn("Block tag manager is not available: #{}", blockTagName);
return;
}

TagKey<Block> tagKey = tagManager.createTagKey(id);
if (!tagManager.isKnownTagName(tagKey)) {
Constants.LOGGER.warn("Unknown block tag in config: #{}", blockTagName);
return;
private static class ForgeBlockResolver implements SoftDeepslateLogic.BlockResolver {
@Override
public Optional<Block> resolveBlock(ResourceLocation id) {
return Optional.ofNullable(ForgeRegistries.BLOCKS.getValue(id));
}

ITag<Block> tag = tagManager.getTag(tagKey);
for (Block block : tag) {
ResourceLocation blockId = ForgeRegistries.BLOCKS.getKey(block);
String blockName = blockId != null ? blockId.toString() : "?";

Constants.LOGGER.debug(
"Caching block from tag #{}: {}{}",
blockTagName,
blockName,
blocks.contains(block) ? " (DUPLICATED)" : ""
);
blocks.add(block);
}
}

public static boolean shouldMineFaster(BlockState state) {
return blocksCache.get().contains(state.getBlock());
}
@Override
public Optional<? extends Iterable<Block>> resolveTag(ResourceLocation id) {
ITagManager<Block> tagManager = ForgeRegistries.BLOCKS.tags();
if (tagManager == null) {
Constants.LOGGER.warn("Block tag manager is not available: #{}", id);
Comment thread
Meatwo310 marked this conversation as resolved.
return Optional.of(List.of());
}

@Mod.EventBusSubscriber(modid = Constants.MODID)
public static class Subscriber {
@SubscribeEvent
public static void onPlayerBreakSpeed(PlayerEvent.BreakSpeed event) {
if (shouldMineFaster(event.getState())) {
event.setNewSpeed((float) (event.getNewSpeed() * ServerConfig.MINING_SPEED.get()));
TagKey<Block> tagKey = tagManager.createTagKey(id);
if (!tagManager.isKnownTagName(tagKey)) {
return Optional.empty();
}

return Optional.of(tagManager.getTag(tagKey));
}

@SubscribeEvent
public static void onTagsUpdated(TagsUpdatedEvent event) {
blocksCache = Lazy.of(ModMain::buildCache);
@Override
public String blockName(Block block) {
ResourceLocation id = ForgeRegistries.BLOCKS.getKey(block);
return id != null ? id.toString() : "?";
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,34 +5,47 @@

import java.util.List;

public class ServerConfig {
public class ServerConfig implements ModServerConfig, ModServerConfigValidator {
public static final ServerConfig INSTANCE = new ServerConfig();

private static final List<String> DEFAULT_BLOCKS = List.of(
"minecraft:deepslate",
"#forge:cobblestone/deepslate",
"#forge:ores_in_ground/deepslate",
"#softdeepslate:building_blocks"
);

private static final ForgeConfigSpec.Builder BUILDER = new ForgeConfigSpec.Builder();

public static ForgeConfigSpec.DoubleValue MINING_SPEED = BUILDER
.comment("""
Adjusts the mining speed for deepslate.
Given that deepslate is twice as hard as stone, a default value of 2.0 allows it to be mined at the same rate as stone.""")
.defineInRange("miningSpeed", 2.0D, 0.0D, Float.MAX_VALUE);
.comment(ModServerConfigEntries.MINING_SPEED.comment())
.defineInRange(
ModServerConfigEntries.MINING_SPEED.key(),
ModServerConfigEntries.MINING_SPEED.defaultValue(),
ModServerConfigEntries.MINING_SPEED.min(),
ModServerConfigEntries.MINING_SPEED.max()
);

public static ForgeConfigSpec.ConfigValue<List<? extends String>> BLOCKS = BUILDER
.comment("""
List of block IDs or block tags (prefix with #) where this mod adjusts the mining speed.
`/reload` to apply.""")
.defineList("blocks", List.of(
"minecraft:deepslate",
"#forge:cobblestone/deepslate",
"#forge:ores_in_ground/deepslate",
"#softdeepslate:building_blocks"
), ServerConfig::isValidIdOrTag);

private static boolean isValidIdOrTag(Object entry) {
return entry instanceof String s && isValidIdOrTag(s);
.comment(ModServerConfigEntries.BLOCKS.comment())
.defineList(ModServerConfigEntries.BLOCKS.key(), DEFAULT_BLOCKS, INSTANCE::isValidIdOrTag);

public static final ForgeConfigSpec SPEC = BUILDER.build();

private ServerConfig() {}

@Override
public double miningSpeed() {
return MINING_SPEED.get();
}

private static boolean isValidIdOrTag(String entry) {
String normalized = entry.startsWith("#") ? entry.substring(1) : entry;
return ResourceLocation.tryParse(normalized) != null;
@Override
public List<? extends String> blocks() {
return BLOCKS.get();
}

public static final ForgeConfigSpec SPEC = BUILDER.build();
@Override
public boolean isValidId(String entry) {
return ResourceLocation.tryParse(entry) != null;
}
}
Loading
Loading