Merge pull request 'feat/3-handle_xp_from_block_breaking' (#4) from feat/3-handle_xp_from_block_breaking into 1.20.1
Reviewed-on: https://gitea.local.micle.dev/minecraft-mods/xp_tools/pulls/4
This commit is contained in:
@ -11,7 +11,7 @@ import org.slf4j.Logger;
|
||||
@Mod(XpTools.MOD_ID)
|
||||
public class XpTools {
|
||||
public static final String MOD_ID = "xp_tools";
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
public static final Logger LOGGER = LogUtils.getLogger();
|
||||
private static FMLJavaModLoadingContext fmlJavaModLoadingContext;
|
||||
private static IProxy proxy;
|
||||
|
||||
|
128
src/main/java/dev/micle/xptools/config/Config.java
Normal file
128
src/main/java/dev/micle/xptools/config/Config.java
Normal file
@ -0,0 +1,128 @@
|
||||
package dev.micle.xptools.config;
|
||||
|
||||
import dev.micle.xptools.XpTools;
|
||||
import dev.micle.xptools.operation.GlobalOperationItem;
|
||||
import dev.micle.xptools.operation.OperationCache;
|
||||
import dev.micle.xptools.operation.OperationItem;
|
||||
import dev.micle.xptools.operation.OperationType;
|
||||
import net.minecraftforge.common.ForgeConfigSpec;
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.config.ModConfig;
|
||||
import net.minecraftforge.fml.event.config.ModConfigEvent;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@Mod.EventBusSubscriber(modid = XpTools.MOD_ID, bus = Mod.EventBusSubscriber.Bus.MOD)
|
||||
public final class Config {
|
||||
public static final Client CLIENT;
|
||||
public static final ForgeConfigSpec CLIENT_SPEC;
|
||||
public static final Common COMMON;
|
||||
public static final ForgeConfigSpec COMMON_SPEC;
|
||||
public static final Server SERVER;
|
||||
public static final ForgeConfigSpec SERVER_SPEC;
|
||||
|
||||
static {
|
||||
Pair<Client, ForgeConfigSpec> clientSpecPair = new ForgeConfigSpec.Builder().configure(Client::new);
|
||||
CLIENT = clientSpecPair.getLeft();
|
||||
CLIENT_SPEC = clientSpecPair.getRight();
|
||||
|
||||
Pair<Common, ForgeConfigSpec> commonSpecPair = new ForgeConfigSpec.Builder().configure(Common::new);
|
||||
COMMON = commonSpecPair.getLeft();
|
||||
COMMON_SPEC = commonSpecPair.getRight();
|
||||
|
||||
Pair<Server, ForgeConfigSpec> serverSpecPair = new ForgeConfigSpec.Builder().configure(Server::new);
|
||||
SERVER = serverSpecPair.getLeft();
|
||||
SERVER_SPEC = serverSpecPair.getRight();
|
||||
}
|
||||
|
||||
public static void register() {
|
||||
XpTools.getFMLJavaModLoadingContext().registerConfig(ModConfig.Type.CLIENT, CLIENT_SPEC);
|
||||
XpTools.getFMLJavaModLoadingContext().registerConfig(ModConfig.Type.COMMON, COMMON_SPEC);
|
||||
XpTools.getFMLJavaModLoadingContext().registerConfig(ModConfig.Type.SERVER, SERVER_SPEC);
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onConfigReloadEvent(ModConfigEvent event) {
|
||||
if (event.getConfig().getSpec() == CLIENT_SPEC) {
|
||||
Client.onConfigReload();
|
||||
} else if (event.getConfig().getSpec() == COMMON_SPEC) {
|
||||
Common.onConfigReload();
|
||||
} else if (event.getConfig().getSpec() == SERVER_SPEC) {
|
||||
Server.onConfigReload();
|
||||
}
|
||||
}
|
||||
|
||||
public static class Client {
|
||||
Client(ForgeConfigSpec.Builder builder) {}
|
||||
|
||||
private static void onConfigReload() {}
|
||||
}
|
||||
|
||||
public static class Common {
|
||||
Common(ForgeConfigSpec.Builder builder) {}
|
||||
|
||||
private static void onConfigReload() {}
|
||||
}
|
||||
|
||||
public static class Server {
|
||||
public static ForgeConfigSpec.BooleanValue debugExtra;
|
||||
|
||||
public static ForgeConfigSpec.BooleanValue optimizationUseCache;
|
||||
|
||||
private static ForgeConfigSpec.ConfigValue<List<? extends String>> blockBreakGlobalOperationsRaw;
|
||||
public static List<GlobalOperationItem> blockBreakGlobalOperationItems;
|
||||
private static ForgeConfigSpec.ConfigValue<List<? extends String>> blockBreakOperationsRaw;
|
||||
public static List<OperationItem> blockBreakOperationItems;
|
||||
|
||||
Server(ForgeConfigSpec.Builder builder) {
|
||||
builder.comment("Settings for debugging").push("debug");
|
||||
debugExtra = builder
|
||||
.comment("Whether to log more extensive debug information.")
|
||||
.define("debugExtra", false);
|
||||
builder.pop();
|
||||
|
||||
builder.comment("Settings for optimizations").push("optimization");
|
||||
optimizationUseCache = builder
|
||||
.comment("When enabled, the list of operations to perform per unique_id will be cached after the first calculation.")
|
||||
.comment("Although this does increase performance at the cost of RAM, the overall performance hit of this mod is tiny anyway... but oh well")
|
||||
.define("optimizationUseCache", true);
|
||||
builder.pop();
|
||||
|
||||
builder.comment("Settings for block breaking").push("block_breaking");
|
||||
builder.comment("Available operations: " + Arrays.toString(OperationType.values()));
|
||||
blockBreakGlobalOperationsRaw = builder
|
||||
.comment("List of global operations. Format: '[operation],[min],[max],[priority]'")
|
||||
.comment("Global operations are run before any unique operations.")
|
||||
.comment("Examples:")
|
||||
.comment("'set,0,0,0' - Sets the xp of all blocks to 0.")
|
||||
.define("blockBreakGlobalOperations", new ArrayList<>());
|
||||
blockBreakOperationsRaw = builder
|
||||
.comment("List of unique operations. Format: '[block_id/tag_id],[operation],[min],[max],[priority],[is_last]'")
|
||||
.comment("Examples:")
|
||||
.comment("'minecraft:dirt,set,2,2,0,true' - Sets the xp drop of the dirt block to 2, takes highest priority and stops any additional operations.")
|
||||
.comment("'#forge:ores,multiply,1,2,1,false' - Multiplies xp drop of all blocks tagged forge:ores by 1-2, allows additional operations.")
|
||||
.define("blockBreakOperations", new ArrayList<>());
|
||||
builder.pop();
|
||||
}
|
||||
|
||||
private static void onConfigReload() {
|
||||
// Clear cache
|
||||
OperationCache.clearBlockBreakCache();
|
||||
|
||||
// Parse all block break global operations
|
||||
blockBreakGlobalOperationItems = new ArrayList<>();
|
||||
for (String s : blockBreakGlobalOperationsRaw.get()) {
|
||||
blockBreakGlobalOperationItems.add(GlobalOperationItem.fromConfig(s));
|
||||
}
|
||||
blockBreakGlobalOperationItems.sort(Comparator.comparingInt(OperationItem::getPriority));
|
||||
|
||||
// Parse all block break unique operations
|
||||
blockBreakOperationItems = new ArrayList<>();
|
||||
for (String s : blockBreakOperationsRaw.get()) {
|
||||
blockBreakOperationItems.add(OperationItem.fromConfig(s));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,123 @@
|
||||
package dev.micle.xptools.events.common;
|
||||
|
||||
import dev.micle.xptools.XpTools;
|
||||
import dev.micle.xptools.config.Config;
|
||||
import dev.micle.xptools.operation.OperationCache;
|
||||
import dev.micle.xptools.operation.OperationItem;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.tags.TagKey;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraftforge.event.level.BlockEvent;
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
import net.minecraftforge.registries.ForgeRegistries;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
public class OnBlockBreakEventHandler {
|
||||
@SubscribeEvent
|
||||
public void OnBlockBreakEvent(BlockEvent.BreakEvent event) {
|
||||
Instant start = Instant.now();
|
||||
float xpToDrop = event.getExpToDrop();
|
||||
|
||||
// Get Block id
|
||||
ResourceLocation block_rl = ForgeRegistries.BLOCKS.getKey(event.getState().getBlock());
|
||||
String block_id = "";
|
||||
if (block_rl != null) {
|
||||
block_id = block_rl.toString();
|
||||
}
|
||||
|
||||
// Collect operations
|
||||
List<OperationItem> operations = null;
|
||||
if (Config.Server.optimizationUseCache.get()) {
|
||||
operations = OperationCache.getBlockBreakCacheEntry(block_id);
|
||||
}
|
||||
|
||||
if (operations == null) {
|
||||
operations = new ArrayList<>();
|
||||
|
||||
// Collect operations on relevant block_id
|
||||
if (!block_id.isEmpty()) {
|
||||
for (OperationItem operationItem : Config.Server.blockBreakOperationItems) {
|
||||
if (!operationItem.isTag() && operationItem.getId().equals(block_id)) {
|
||||
operations.add(operationItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Collect operations on relevant tag_id
|
||||
for (TagKey<Block> tagKey : event.getState().getTags().toList()) {
|
||||
String tag_id = tagKey.location().toString();
|
||||
for (OperationItem operationItem : Config.Server.blockBreakOperationItems) {
|
||||
if (operationItem.isTag() && operationItem.getId().equals(tag_id)) {
|
||||
operations.add(operationItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sort operations based on priority
|
||||
operations.sort(Comparator.comparingInt(OperationItem::getPriority));
|
||||
|
||||
// Remove any operations after last operation
|
||||
for (OperationItem operationItem : operations) {
|
||||
if (operationItem.isLast()) {
|
||||
operations = operations.subList(0, operations.indexOf(operationItem) + 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Save operations to cache
|
||||
OperationCache.addBlockBreakCacheEntry(block_id, operations);
|
||||
}
|
||||
|
||||
// Add global operations before all others
|
||||
operations.addAll(0, Config.Server.blockBreakGlobalOperationItems);
|
||||
|
||||
// Apply operations to xp drops
|
||||
for (OperationItem operation : operations) {
|
||||
// Calculate operation value
|
||||
float opValue = (operation.getMin() == operation.getMax()) ?
|
||||
operation.getMin() :
|
||||
ThreadLocalRandom.current().nextFloat(operation.getMin(), operation.getMax());
|
||||
|
||||
// Apply operation
|
||||
switch (operation.getType()) {
|
||||
case SET:
|
||||
xpToDrop = opValue;
|
||||
break;
|
||||
case ADD:
|
||||
xpToDrop += opValue;
|
||||
break;
|
||||
case SUBTRACT:
|
||||
xpToDrop -= opValue;
|
||||
break;
|
||||
case MULTIPLY:
|
||||
xpToDrop *= opValue;
|
||||
break;
|
||||
case DIVIDE:
|
||||
xpToDrop /= opValue;
|
||||
break;
|
||||
}
|
||||
|
||||
// Stop if this is the last operation
|
||||
if (operation.isLast()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Debug logging
|
||||
if (Config.Server.debugExtra.get()) {
|
||||
XpTools.LOGGER.debug("Completed block break event:");
|
||||
XpTools.LOGGER.debug("\tOperations: {}", operations);
|
||||
XpTools.LOGGER.debug("\tTime taken (nano seconds): {}", Duration.between(start, Instant.now()).toNanos());
|
||||
XpTools.LOGGER.debug("\tXP: {} -> {}", event.getExpToDrop(), xpToDrop);
|
||||
}
|
||||
|
||||
// Apply xp drop
|
||||
event.setExpToDrop((int)xpToDrop);
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
package dev.micle.xptools.operation;
|
||||
|
||||
import dev.micle.xptools.util.EnumUtils;
|
||||
|
||||
public class GlobalOperationItem extends OperationItem {
|
||||
public GlobalOperationItem(OperationType type, float min, float max, int priority) {
|
||||
super(false, "", type, min, max, priority, false);
|
||||
}
|
||||
|
||||
public static GlobalOperationItem fromConfig(String configString) {
|
||||
String[] splitString = configString.split(",");
|
||||
|
||||
if (splitString.length == 4) {
|
||||
OperationType type = EnumUtils.valueOf(OperationType.class, splitString[0]);
|
||||
float min = Float.parseFloat(splitString[1]);
|
||||
float max = Float.parseFloat(splitString[2]);
|
||||
int priority = Integer.parseInt(splitString[3]);
|
||||
|
||||
return new GlobalOperationItem(type, min, max, priority);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%s,%f,%f,%d", getType().toString(), getMin(), getMax(), getPriority());
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
package dev.micle.xptools.operation;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
public class OperationCache {
|
||||
private static HashMap<String, List<OperationItem>> blockBreakCache;
|
||||
|
||||
public static void clearBlockBreakCache() {
|
||||
blockBreakCache = new HashMap<>();
|
||||
}
|
||||
|
||||
public static @Nullable List<OperationItem> getBlockBreakCacheEntry(String key) {
|
||||
if (blockBreakCache.containsKey(key)) {
|
||||
return new ArrayList<>(blockBreakCache.get(key));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void addBlockBreakCacheEntry(String key, List<OperationItem> value) {
|
||||
blockBreakCache.putIfAbsent(key, new ArrayList<>(value));
|
||||
}
|
||||
}
|
84
src/main/java/dev/micle/xptools/operation/OperationItem.java
Normal file
84
src/main/java/dev/micle/xptools/operation/OperationItem.java
Normal file
@ -0,0 +1,84 @@
|
||||
package dev.micle.xptools.operation;
|
||||
|
||||
import dev.micle.xptools.util.EnumUtils;
|
||||
|
||||
public class OperationItem {
|
||||
private final boolean isTag;
|
||||
private final String id;
|
||||
private final OperationType type;
|
||||
private final float min;
|
||||
private final float max;
|
||||
private final int priority;
|
||||
private final boolean isLast;
|
||||
|
||||
public OperationItem(boolean isTag, String id, OperationType type, float min, float max, int priority, boolean isLast) {
|
||||
this.isTag = isTag;
|
||||
this.id = id;
|
||||
this.type = type;
|
||||
this.min = min;
|
||||
this.max = max;
|
||||
this.priority = priority;
|
||||
this.isLast = isLast;
|
||||
}
|
||||
|
||||
public static OperationItem fromConfig(String configString) {
|
||||
String[] splitString = configString.split(",");
|
||||
|
||||
if (splitString.length == 6) {
|
||||
boolean isTag = splitString[0].startsWith("#");
|
||||
String id = isTag ? splitString[0].substring(1) : splitString[0];
|
||||
OperationType type = EnumUtils.valueOf(OperationType.class, splitString[1]);
|
||||
float min = Float.parseFloat(splitString[2]);
|
||||
float max = Float.parseFloat(splitString[3]);
|
||||
int priority = Integer.parseInt(splitString[4]);
|
||||
boolean isLast = Boolean.parseBoolean(splitString[5]);
|
||||
|
||||
return new OperationItem(isTag, id, type, min, max, priority, isLast);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
if (isTag) {
|
||||
builder.append("#");
|
||||
}
|
||||
builder
|
||||
.append(id).append(",")
|
||||
.append(type.toString()).append(",")
|
||||
.append(min).append(",").append(max).append(",")
|
||||
.append(priority).append(",")
|
||||
.append(isLast);
|
||||
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
public boolean isTag() {
|
||||
return isTag;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public OperationType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public float getMin() {
|
||||
return min;
|
||||
}
|
||||
|
||||
public float getMax() {
|
||||
return max;
|
||||
}
|
||||
|
||||
public int getPriority() {
|
||||
return priority;
|
||||
}
|
||||
|
||||
public boolean isLast() {
|
||||
return isLast;
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package dev.micle.xptools.operation;
|
||||
|
||||
public enum OperationType {
|
||||
SET,
|
||||
ADD,
|
||||
SUBTRACT,
|
||||
MULTIPLY,
|
||||
DIVIDE
|
||||
}
|
@ -1,6 +1,8 @@
|
||||
package dev.micle.xptools.proxy;
|
||||
|
||||
import dev.micle.xptools.XpTools;
|
||||
import dev.micle.xptools.config.Config;
|
||||
import dev.micle.xptools.events.common.OnBlockBreakEventHandler;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
@ -17,6 +19,8 @@ public class Proxy implements IProxy {
|
||||
|
||||
// Common setup
|
||||
public Proxy() {
|
||||
Config.register();
|
||||
|
||||
// Register mod event bus listeners
|
||||
IEventBus modEventBus = XpTools.getFMLJavaModLoadingContext().getModEventBus();
|
||||
modEventBus.addListener(Proxy::setup);
|
||||
@ -27,6 +31,9 @@ public class Proxy implements IProxy {
|
||||
MinecraftForge.EVENT_BUS.addListener(Proxy::onAddReloadListeners);
|
||||
MinecraftForge.EVENT_BUS.addListener(Proxy::serverStarted);
|
||||
MinecraftForge.EVENT_BUS.addListener(Proxy::serverStopping);
|
||||
|
||||
// Register event handlers
|
||||
MinecraftForge.EVENT_BUS.register(new OnBlockBreakEventHandler());
|
||||
}
|
||||
|
||||
private static void setup(FMLCommonSetupEvent event) {}
|
||||
|
17
src/main/java/dev/micle/xptools/util/EnumUtils.java
Normal file
17
src/main/java/dev/micle/xptools/util/EnumUtils.java
Normal file
@ -0,0 +1,17 @@
|
||||
package dev.micle.xptools.util;
|
||||
|
||||
public class EnumUtils {
|
||||
public static <T extends Enum<?>> T valueOf(Class<T> clazz, String name) {
|
||||
for (T e : clazz.getEnumConstants()) {
|
||||
if (e.name().equalsIgnoreCase(name)) {
|
||||
return e;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static <T extends Enum<?>> T valueOfOrDefault(Class<T> clazz, String name, T defaultValue) {
|
||||
T value = valueOf(clazz, name);
|
||||
return (value == null) ? defaultValue : value;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user