Ex Deorum 1.9

This commit is contained in:
thedarkcolour 2023-11-14 00:07:01 -08:00
parent e92626ac6a
commit 89b0c0d63f
11 changed files with 180 additions and 13 deletions

View File

@ -5,7 +5,7 @@ plugins {
id 'org.spongepowered.mixin' version '0.7.+'
}
version = '1.8'
version = '1.9'
group = 'thedarkcolour.exdeorum'
base {
archivesName = 'exdeorum'

View File

@ -1,3 +1,9 @@
## Ex Deorum 1.9
- Fixed incompatibility with SkyblockBuilder where player would not receive torch/watering can and Ex Deorum advancements
- Fixed error message printing while patching the End Portal method
- Fixed several issues with hammers in LAN and server worlds
- Fixed hammer recipes with tag ingredients not functioning
## Ex Deorum 1.8
- Added a config option to limit the number of sieve drops from sieving moss. May be useful when playing with mods that add a lot of different saplings.
- Added compatibility with Ars Nouveau's saplings and Sourceberries to the sieve.

View File

@ -35,6 +35,8 @@ import net.minecraftforge.client.event.RegisterClientReloadListenersEvent;
import net.minecraftforge.client.event.RegisterShadersEvent;
import net.minecraftforge.client.event.ScreenEvent;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.event.TagsUpdatedEvent;
import net.minecraftforge.eventbus.api.Event;
import net.minecraftforge.fml.event.config.ModConfigEvent;
import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent;
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
@ -45,6 +47,7 @@ import thedarkcolour.exdeorum.client.ter.InfestedLeavesRenderer;
import thedarkcolour.exdeorum.client.ter.SieveRenderer;
import thedarkcolour.exdeorum.config.EConfig;
import thedarkcolour.exdeorum.network.ClientMessageHandler;
import thedarkcolour.exdeorum.recipe.RecipeUtil;
import thedarkcolour.exdeorum.registry.EBlockEntities;
import thedarkcolour.exdeorum.registry.EFluids;
import thedarkcolour.exdeorum.registry.EWorldPresets;
@ -52,6 +55,8 @@ import thedarkcolour.exdeorum.registry.EWorldPresets;
import java.io.IOException;
public class ClientHandler {
public static boolean needsRecipeCacheRefresh;
public static void register() {
var modBus = FMLJavaModLoadingContext.get().getModEventBus();
var fmlBus = MinecraftForge.EVENT_BUS;
@ -60,10 +65,18 @@ public class ClientHandler {
modBus.addListener(ClientHandler::registerRenderers);
modBus.addListener(ClientHandler::registerShaders);
modBus.addListener(ClientHandler::addClientReloadListeners);
modBus.addListener(ClientHandler::onConfigChanged);
fmlBus.addListener(ClientHandler::onPlayerRespawn);
fmlBus.addListener(ClientHandler::onPlayerLogout);
modBus.addListener(ClientHandler::onConfigChanged);
fmlBus.addListener(ClientHandler::onScreenOpen);
fmlBus.addListener(ClientHandler::onTagsUpdated);
}
private static void onTagsUpdated(TagsUpdatedEvent event) {
if (needsRecipeCacheRefresh && Minecraft.getInstance().getConnection() != null) {
RecipeUtil.reload(Minecraft.getInstance().getConnection().getRecipeManager());
needsRecipeCacheRefresh = false;
}
}
private static void addClientReloadListeners(RegisterClientReloadListenersEvent event) {
@ -84,6 +97,12 @@ public class ClientHandler {
private static void onPlayerLogout(ClientPlayerNetworkEvent.LoggingOut event) {
ClientMessageHandler.isInVoidWorld = false;
needsRecipeCacheRefresh = false;
// Only null when logging in
if (Minecraft.getInstance().level != null) {
RecipeUtil.unload();
}
}
private static void onConfigChanged(ModConfigEvent.Reloading event) {

View File

@ -0,0 +1,30 @@
/*
* Ex Deorum
* Copyright (c) 2023 thedarkcolour
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package thedarkcolour.exdeorum.event;
import net.minecraft.client.Minecraft;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.Level;
// necessary to avoid EventBus loading LocalPlayer through its ASM transformations
class ClientsideCode {
static Player getLocalPlayer() {
return Minecraft.getInstance().player;
}
}

View File

@ -29,6 +29,7 @@ import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.util.Unit;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.level.GameRules;
@ -36,18 +37,23 @@ import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.levelgen.Heightmap;
import net.minecraft.world.level.levelgen.XoroshiroRandomSource;
import net.minecraft.world.level.levelgen.feature.ConfiguredFeature;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.client.event.ClientChatEvent;
import net.minecraftforge.common.ForgeMod;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.event.AddReloadListenerEvent;
import net.minecraftforge.event.OnDatapackSyncEvent;
import net.minecraftforge.event.entity.player.PlayerEvent;
import net.minecraftforge.event.level.LevelEvent;
import net.minecraftforge.fluids.FluidInteractionRegistry;
import net.minecraftforge.fml.DistExecutor;
import net.minecraftforge.fml.InterModComms;
import net.minecraftforge.fml.ModList;
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent;
import net.minecraftforge.fml.event.lifecycle.InterModEnqueueEvent;
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
import net.minecraftforge.fml.loading.FMLEnvironment;
import org.jetbrains.annotations.Nullable;
import thedarkcolour.exdeorum.ExDeorum;
import thedarkcolour.exdeorum.blockentity.LavaCrucibleBlockEntity;
import thedarkcolour.exdeorum.client.CompostColors;
@ -56,6 +62,7 @@ import thedarkcolour.exdeorum.compat.top.ExDeorumTopCompat;
import thedarkcolour.exdeorum.config.EConfig;
import thedarkcolour.exdeorum.item.HammerItem;
import thedarkcolour.exdeorum.item.WateringCanItem;
import thedarkcolour.exdeorum.network.ClientMessageHandler;
import thedarkcolour.exdeorum.network.NetworkHandler;
import thedarkcolour.exdeorum.recipe.RecipeUtil;
import thedarkcolour.exdeorum.registry.EFluids;
@ -63,15 +70,21 @@ import thedarkcolour.exdeorum.registry.EItems;
import thedarkcolour.exdeorum.tag.EBiomeTags;
import thedarkcolour.exdeorum.voidworld.VoidChunkGenerator;
import java.util.HashSet;
import java.util.Locale;
import java.util.Set;
import java.util.UUID;
public final class EventHandler {
public static void register() {
var fmlBus = MinecraftForge.EVENT_BUS;
var modBus = FMLJavaModLoadingContext.get().getModEventBus();
fmlBus.addListener(EventHandler::onPlayerLogin);
fmlBus.addListener(EventHandler::onDataSynced);
fmlBus.addListener(EventHandler::addReloadListeners);
modBus.addListener(EventHandler::interModEnqueue);
fmlBus.addListener(EventHandler::createSpawnTree);
modBus.addListener(EventHandler::interModEnqueue);
modBus.addListener(EventHandler::onCommonSetup);
if (ExDeorum.DEBUG) {
@ -154,9 +167,38 @@ public final class EventHandler {
});
}
private static void onDataSynced(OnDatapackSyncEvent event) {
UUID excludedUUID = null;
if (FMLEnvironment.dist == Dist.CLIENT) {
// since event code is turned into ASM, we need this to prevent ASM trying to load the LocalPlayer class
Player player = DistExecutor.unsafeCallWhenOn(Dist.CLIENT, () -> ClientsideCode::getLocalPlayer);;
if (player == null) {
return;
} else {
excludedUUID = player.getUUID();
}
}
// A player who is first connecting isn't yet included in the server's player list, so include them.
Set<ServerPlayer> players = new HashSet<>(event.getPlayerList().getPlayers());
if (event.getPlayer() != null) {
players.add(event.getPlayer());
}
for (var player : players) {
if (!player.getUUID().equals(excludedUUID)) {
NetworkHandler.sendRecipeCacheReset(player);
}
}
}
private static void onPlayerLogin(PlayerEvent.PlayerLoggedInEvent event) {
if (event.getEntity() instanceof ServerPlayer player) {
if (player.serverLevel().getChunkSource().getGenerator() instanceof VoidChunkGenerator) {
var generator = player.serverLevel().getChunkSource().getGenerator();
// tries to account for other SkyBlock generator mods like SkyBlockBuilder
if (generator instanceof VoidChunkGenerator || generator.getClass().getName().toLowerCase(Locale.ROOT).contains("skyblock")) {
NetworkHandler.sendVoidWorld(player);
var advancement = player.server.getAdvancements().getAdvancement(new ResourceLocation(ExDeorum.ID, "core/root"));
@ -188,7 +230,6 @@ public final class EventHandler {
var recipes = event.getServerResources().getRecipeManager();
event.addListener((prepBarrier, resourceManager, prepProfiler, reloadProfiler, backgroundExecutor, gameExecutor) -> {
return prepBarrier.wait(Unit.INSTANCE).thenRunAsync(() -> {
HammerItem.refreshValidBlocks(recipes);
RecipeUtil.reload(recipes);
LavaCrucibleBlockEntity.putDefaultHeatValues();
}, gameExecutor);

View File

@ -28,32 +28,41 @@ import net.minecraft.world.item.crafting.RecipeManager;
import net.minecraft.world.item.crafting.RecipeType;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraftforge.common.util.Lazy;
import org.jetbrains.annotations.Nullable;
import thedarkcolour.exdeorum.recipe.RecipeUtil;
import thedarkcolour.exdeorum.registry.EItems;
import thedarkcolour.exdeorum.registry.ERecipeTypes;
import java.util.Set;
public class HammerItem extends DiggerItem {
public static final Set<Block> VALID_BLOCKS = new ObjectOpenHashSet<>();
public static Lazy<Set<Block>> validBlocks = Lazy.of(HammerItem::computeValidBlocks);
public HammerItem(Tier tier, Properties properties) {
super(1.0f, -2.8f, tier, null, properties);
}
public static void refreshValidBlocks(RecipeManager recipes) {
VALID_BLOCKS.clear();
for (var recipe : recipes.byType(ERecipeTypes.HAMMER.get()).values()) {
public static Set<Block> computeValidBlocks() {
var hammerRecipes = RecipeUtil.getCachedHammerRecipes();
var validBlocks = new ObjectOpenHashSet<Block>(hammerRecipes.size());
for (var recipe : hammerRecipes) {
for (var item : recipe.getIngredient().getItems()) {
if (item.getItem() instanceof BlockItem blockItem) {
VALID_BLOCKS.add(blockItem.getBlock());
validBlocks.add(blockItem.getBlock());
}
}
}
return validBlocks;
}
public static void refreshValidBlocks(RecipeManager recipes) {
validBlocks = Lazy.of(HammerItem::computeValidBlocks);
}
protected Set<Block> getValidBlocks() {
return VALID_BLOCKS;
return validBlocks.get();
}
@Override

View File

@ -19,6 +19,8 @@
package thedarkcolour.exdeorum.network;
import net.minecraft.client.Minecraft;
import thedarkcolour.exdeorum.client.ClientHandler;
import thedarkcolour.exdeorum.recipe.RecipeUtil;
public class ClientMessageHandler {
public static boolean isInVoidWorld;
@ -32,4 +34,9 @@ public class ClientMessageHandler {
level.clientLevelData.isFlat = true;
}
}
public static void reloadClientRecipeCache() {
//RecipeUtil.reload(Minecraft.getInstance().level.getRecipeManager());
ClientHandler.needsRecipeCacheRefresh = true;
}
}

View File

@ -35,12 +35,17 @@ public final class NetworkHandler {
public static void register() {
CHANNEL.registerMessage(0, VoidWorldMessage.class, VoidWorldMessage::encode, VoidWorldMessage::decode, VoidWorldMessage::handle);
CHANNEL.registerMessage(1, PlayerDataMessage.class, (msg, buffer) -> {}, buffer -> new PlayerDataMessage(), PlayerDataMessage::handle);
}
public static void sendVoidWorld(ServerPlayer pPlayer) {
CHANNEL.sendTo(new VoidWorldMessage(), pPlayer.connection.connection, NetworkDirection.PLAY_TO_CLIENT);
}
public static void sendRecipeCacheReset(ServerPlayer player) {
CHANNEL.sendTo(new PlayerDataMessage(), player.connection.connection, NetworkDirection.PLAY_TO_CLIENT);
}
static void handle(Supplier<NetworkEvent.Context> ctxSupplier, Consumer<NetworkEvent.Context> handler) {
var ctx = ctxSupplier.get();
ctx.enqueueWork(() -> handler.accept(ctx));

View File

@ -0,0 +1,36 @@
/*
* Ex Deorum
* Copyright (c) 2023 thedarkcolour
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package thedarkcolour.exdeorum.network;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.fml.DistExecutor;
import net.minecraftforge.network.NetworkEvent;
import java.util.function.Supplier;
// Server -> Client
// Fired whenever a player joins an LAN world or dedicated server to load the recipe caches
// Also fired whenever those servers reload data
public class PlayerDataMessage {
public void handle(Supplier<NetworkEvent.Context> ctxSupplier) {
NetworkHandler.handle(ctxSupplier, ctx -> {
DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> ClientMessageHandler::reloadClientRecipeCache);
});
}
}

View File

@ -47,6 +47,7 @@ import net.minecraftforge.common.util.Lazy;
import net.minecraftforge.fluids.FluidStack;
import org.jetbrains.annotations.Nullable;
import thedarkcolour.exdeorum.compat.PreferredOres;
import thedarkcolour.exdeorum.item.HammerItem;
import thedarkcolour.exdeorum.recipe.barrel.BarrelCompostRecipe;
import thedarkcolour.exdeorum.recipe.barrel.BarrelMixingRecipe;
import thedarkcolour.exdeorum.recipe.crucible.CrucibleRecipe;
@ -77,6 +78,15 @@ public final class RecipeUtil {
lavaCrucibleRecipeCache = loadSimpleRecipeCache(recipes, ERecipeTypes.LAVA_CRUCIBLE);
waterCrucibleRecipeCache = loadSimpleRecipeCache(recipes, ERecipeTypes.WATER_CRUCIBLE);
hammerRecipeCache = loadSimpleRecipeCache(recipes, ERecipeTypes.HAMMER);
HammerItem.refreshValidBlocks(recipes);
}
public static void unload() {
SIEVE_RECIPE_CACHE.invalidateAll();
barrelCompostRecipeCache = null;
lavaCrucibleRecipeCache = null;
waterCrucibleRecipeCache = null;
hammerRecipeCache = null;
}
private static <T extends SingleIngredientRecipe> Lazy<Map<Item, T>> loadSimpleRecipeCache(RecipeManager recipes, Supplier<RecipeType<T>> recipeType) {
@ -138,6 +148,10 @@ public final class RecipeUtil {
return hammerRecipeCache.get().get(playerItem);
}
public static Collection<HammerRecipe> getCachedHammerRecipes() {
return hammerRecipeCache.get().values();
}
public static <C extends Container, T extends Recipe<C>> Collection<T> byType(RecipeManager manager, RecipeType<T> type) {
return manager.byType(type).values();
}

View File

@ -107,7 +107,7 @@ function initializeCoreMod() {
new FieldInsnNode(Opcodes.PUTFIELD, insn.owner, insn.name, insn.desc)
));
ASMAPI.log('INFO', 'The node we are patching at: { opcode: ' + insn.getOpcode() + ', name: ' + insn.getName());
ASMAPI.log('INFO', 'The node we are patching at: { opcode: ' + insn.getOpcode() + ', name: ' + insn.name);
ASMAPI.log('INFO', 'Successfully patched end portal.');
return method;