diff --git a/.gitignore b/.gitignore index 1e74fe00..a23c7d50 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ bin .classpath .project +.claude # idea out *.ipr @@ -46,4 +47,4 @@ src/generated/ neoforge-main/src/generated/ -neoforge-main/runs/ +neoforge-main/runs/ \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index af05e612..1d744211 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -7,5 +7,5 @@ module = "io.reactivex.rxjava3:rxjava" version = "3.1.5" [libraries.reactivestreams] -module = "io.reactivex.rxjava3:reactive-streams" -version = "[1.0.4,)" \ No newline at end of file +module = "org.reactivestreams:reactive-streams" +version = "1.0.4" \ No newline at end of file diff --git a/neoforge-datagen/src/main/java/dev/compactmods/crafting/datagen/BlockLootGenerator.java b/neoforge-datagen/src/main/java/dev/compactmods/crafting/datagen/BlockLootGenerator.java index a1efaeb8..2a92da17 100644 --- a/neoforge-datagen/src/main/java/dev/compactmods/crafting/datagen/BlockLootGenerator.java +++ b/neoforge-datagen/src/main/java/dev/compactmods/crafting/datagen/BlockLootGenerator.java @@ -24,9 +24,9 @@ public BlockLootGenerator(HolderLookup.Provider holderLookup) { @Override protected Iterable getKnownBlocks() { return ImmutableList.of( - CCBlocks.FIELD_PROJECTOR_BLOCK.get() -// CCBlocks.MATCH_FIELD_PROXY_BLOCK.get(), -// CCBlocks.RESCAN_FIELD_PROXY_BLOCK.get() + CCBlocks.FIELD_PROJECTOR_BLOCK.get(), + CCBlocks.MATCH_FIELD_PROXY_BLOCK.get(), + CCBlocks.RESCAN_FIELD_PROXY_BLOCK.get() ); } @@ -38,5 +38,18 @@ protected void generate() { .setRolls(ConstantValue.exactly(1)) .when(ExplosionCondition.survivesExplosion()) .add(LootItem.lootTableItem(CCItems.FIELD_PROJECTOR_ITEM.get())))); + + this.add(CCBlocks.MATCH_FIELD_PROXY_BLOCK.get(), LootTable.lootTable().withPool(LootPool + .lootPool() + .name(CCBlocks.MATCH_FIELD_PROXY_BLOCK.getId().toString()) + .setRolls(ConstantValue.exactly(1)) + .when(ExplosionCondition.survivesExplosion()) + .add(LootItem.lootTableItem(CCItems.MATCH_PROXY_ITEM.get())))); + this.add(CCBlocks.RESCAN_FIELD_PROXY_BLOCK.get(), LootTable.lootTable().withPool(LootPool + .lootPool() + .name(CCBlocks.RESCAN_FIELD_PROXY_BLOCK.getId().toString()) + .setRolls(ConstantValue.exactly(1)) + .when(ExplosionCondition.survivesExplosion()) + .add(LootItem.lootTableItem(CCItems.RESCAN_PROXY_ITEM.get())))); } } diff --git a/neoforge-datagen/src/main/java/dev/compactmods/crafting/datagen/BlockTagGenerator.java b/neoforge-datagen/src/main/java/dev/compactmods/crafting/datagen/BlockTagGenerator.java new file mode 100644 index 00000000..274a7f09 --- /dev/null +++ b/neoforge-datagen/src/main/java/dev/compactmods/crafting/datagen/BlockTagGenerator.java @@ -0,0 +1,32 @@ +package dev.compactmods.crafting.datagen; + +import dev.compactmods.crafting.CompactCrafting; +import dev.compactmods.crafting.core.CCBlocks; +import net.minecraft.core.HolderLookup; +import net.minecraft.data.PackOutput; +import net.minecraft.tags.BlockTags; +import net.neoforged.neoforge.common.data.BlockTagsProvider; +import net.neoforged.neoforge.common.data.ExistingFileHelper; +import org.jetbrains.annotations.Nullable; + +import java.util.concurrent.CompletableFuture; + +public class BlockTagGenerator extends BlockTagsProvider { + + public BlockTagGenerator(PackOutput output, CompletableFuture lookupProvider, @Nullable ExistingFileHelper existingFileHelper) { + super(output, lookupProvider, CompactCrafting.MOD_ID, existingFileHelper); + } + + @Override + protected void addTags(HolderLookup.Provider provider) { + tag(BlockTags.MINEABLE_WITH_PICKAXE) + .add(CCBlocks.FIELD_PROJECTOR_BLOCK.get()) + .add(CCBlocks.MATCH_FIELD_PROXY_BLOCK.get()) + .add(CCBlocks.RESCAN_FIELD_PROXY_BLOCK.get()); + + tag(BlockTags.NEEDS_DIAMOND_TOOL) + .add(CCBlocks.FIELD_PROJECTOR_BLOCK.get()) + .add(CCBlocks.MATCH_FIELD_PROXY_BLOCK.get()) + .add(CCBlocks.RESCAN_FIELD_PROXY_BLOCK.get()); + } +} diff --git a/neoforge-datagen/src/main/java/dev/compactmods/crafting/datagen/DataGeneration.java b/neoforge-datagen/src/main/java/dev/compactmods/crafting/datagen/DataGeneration.java index c4fdbe1b..5e2dfe23 100644 --- a/neoforge-datagen/src/main/java/dev/compactmods/crafting/datagen/DataGeneration.java +++ b/neoforge-datagen/src/main/java/dev/compactmods/crafting/datagen/DataGeneration.java @@ -34,6 +34,7 @@ private static void registerServerProviders(DataGenerator generator, GatherDataE )); generator.addProvider(event.includeServer(), new RecipeGenerator(pack, lookup)); + generator.addProvider(event.includeServer(), new BlockTagGenerator(pack, lookup, event.getExistingFileHelper())); } private static void registerClientProviders(DataGenerator generator, GatherDataEvent event) { @@ -42,6 +43,6 @@ private static void registerClientProviders(DataGenerator generator, GatherDataE generator.addProvider(event.includeClient(), new SharedStateGenerator(pack, event.getExistingFileHelper())); generator.addProvider(event.includeClient(), new ProjectorStateGenerator(pack, event.getExistingFileHelper())); -// generator.addProvider(event.includeClient(), new ProxyStateGenerator(generator, event.getExistingFileHelper())); + generator.addProvider(event.includeClient(), new ProxyStateGenerator(pack, event.getExistingFileHelper())); } } diff --git a/neoforge-datagen/src/main/java/dev/compactmods/crafting/datagen/ProxyStateGenerator.java b/neoforge-datagen/src/main/java/dev/compactmods/crafting/datagen/ProxyStateGenerator.java index 2fb0a040..9f04fc5f 100644 --- a/neoforge-datagen/src/main/java/dev/compactmods/crafting/datagen/ProxyStateGenerator.java +++ b/neoforge-datagen/src/main/java/dev/compactmods/crafting/datagen/ProxyStateGenerator.java @@ -1,50 +1,50 @@ -//package dev.compactmods.crafting.datagen; -// -//import dev.compactmods.crafting.CompactCrafting; -//import dev.compactmods.crafting.core.CCBlocks; -//import net.minecraft.client.renderer.block.model.ItemTransforms; -//import net.minecraft.data.DataGenerator; -//import net.minecraft.data.PackOutput; -//import net.neoforged.neoforge.client.model.generators.BlockStateProvider; -//import net.neoforged.neoforge.common.data.ExistingFileHelper; -// -//public class ProxyStateGenerator extends BlockStateProvider { -// -// public ProxyStateGenerator(PackOutput pack, ExistingFileHelper files) { -// super(pack, CompactCrafting.MOD_ID, files); -// } -// -// @Override -// protected void registerStatesAndModels() { -// getVariantBuilder(CCBlocks.MATCH_FIELD_PROXY_BLOCK.get()) -// .forAllStates(state -> ConfiguredModel.builder() -// .modelFile(models().getExistingFile(modLoc("block/base"))) -// .build()); -// -// getVariantBuilder(CCBlocks.RESCAN_FIELD_PROXY_BLOCK.get()) -// .forAllStates(state -> ConfiguredModel.builder() -// .modelFile(models().getExistingFile(modLoc("block/base"))) -// .build()); -// -// SharedStateGenerator.addProjectorBase(models().getBuilder("match_proxy")); -// SharedStateGenerator.addProjectorBase(models().getBuilder("rescan_proxy")); -// -// itemModels() -// .withExistingParent("match_proxy", modLoc("block/match_proxy")) -// .transforms() -// .transform(ItemTransforms.TransformType.GUI) -// .rotation(33.75f, 45f, 0) -// .translation(0, 1, 0) -// .scale(0.6f, 0.6f, 0.6f) -// .end(); -// -// itemModels() -// .withExistingParent("rescan_proxy", modLoc("block/rescan_proxy")) -// .transforms() -// .transform(ItemTransforms.TransformType.GUI) -// .rotation(33.75f, 45f, 0) -// .translation(0, 1, 0) -// .scale(0.6f, 0.6f, 0.6f) -// .end(); -// } -//} +package dev.compactmods.crafting.datagen; + +import dev.compactmods.crafting.CompactCrafting; +import dev.compactmods.crafting.core.CCBlocks; +import net.minecraft.data.PackOutput; +import net.minecraft.world.item.ItemDisplayContext; +import net.neoforged.neoforge.client.model.generators.BlockStateProvider; +import net.neoforged.neoforge.client.model.generators.ConfiguredModel; +import net.neoforged.neoforge.common.data.ExistingFileHelper; + +public class ProxyStateGenerator extends BlockStateProvider { + + public ProxyStateGenerator(PackOutput pack, ExistingFileHelper files) { + super(pack, CompactCrafting.MOD_ID, files); + } + + @Override + protected void registerStatesAndModels() { + getVariantBuilder(CCBlocks.MATCH_FIELD_PROXY_BLOCK.get()) + .forAllStates(state -> ConfiguredModel.builder() + .modelFile(models().getExistingFile(modLoc("block/base"))) + .build()); + + getVariantBuilder(CCBlocks.RESCAN_FIELD_PROXY_BLOCK.get()) + .forAllStates(state -> ConfiguredModel.builder() + .modelFile(models().getExistingFile(modLoc("block/base"))) + .build()); + + SharedStateGenerator.addProjectorBase(models().getBuilder("match_proxy")); + SharedStateGenerator.addProjectorBase(models().getBuilder("rescan_proxy")); + + itemModels() + .withExistingParent("match_proxy", modLoc("block/match_proxy")) + .transforms() + .transform(ItemDisplayContext.GUI) + .rotation(33.75f, 45f, 0) + .translation(0, 1, 0) + .scale(0.6f, 0.6f, 0.6f) + .end(); + + itemModels() + .withExistingParent("rescan_proxy", modLoc("block/rescan_proxy")) + .transforms() + .transform(ItemDisplayContext.GUI) + .rotation(33.75f, 45f, 0) + .translation(0, 1, 0) + .scale(0.6f, 0.6f, 0.6f) + .end(); + } +} diff --git a/neoforge-datagen/src/main/java/dev/compactmods/crafting/datagen/RecipeGenerator.java b/neoforge-datagen/src/main/java/dev/compactmods/crafting/datagen/RecipeGenerator.java index 89036ecd..34648820 100644 --- a/neoforge-datagen/src/main/java/dev/compactmods/crafting/datagen/RecipeGenerator.java +++ b/neoforge-datagen/src/main/java/dev/compactmods/crafting/datagen/RecipeGenerator.java @@ -47,16 +47,17 @@ protected void buildRecipes(RecipeOutput output, HolderLookup.Provider provider) .unlockedBy("got_ender_eye", has(Items.ENDER_EYE)) .save(output); -// ShapelessRecipeBuilder.shapeless(CCItems.MATCH_PROXY_ITEM.get()) -// .requires(CCItems.BASE_ITEM.get()) -// .requires(Items.REDSTONE) -// .unlockedBy("got_redstone", has(Items.REDSTONE)) -// .save(consumer); -// -// ShapelessRecipeBuilder.shapeless(CCItems.RESCAN_PROXY_ITEM.get()) -// .requires(CCItems.BASE_ITEM.get()) -// .requires(Items.CRAFTING_TABLE) -// .unlockedBy("got_crafting_table", has(Items.CRAFTING_TABLE)) -// .save(consumer); + ShapelessRecipeBuilder.shapeless(RecipeCategory.MISC,CCItems.MATCH_PROXY_ITEM.get()) + .requires(CCItems.BASE_ITEM.get()) + .requires(Items.REDSTONE) + .unlockedBy("got_redstone", has(Items.REDSTONE)) + .save(output); + + + ShapelessRecipeBuilder.shapeless(RecipeCategory.MISC,CCItems.RESCAN_PROXY_ITEM.get()) + .requires(CCItems.BASE_ITEM.get()) + .requires(Items.CRAFTING_TABLE) + .unlockedBy("got_crafting_table", has(Items.CRAFTING_TABLE)) + .save(output); } } diff --git a/neoforge-main/build.gradle.kts b/neoforge-main/build.gradle.kts index 34e9142e..bfbf2f31 100644 --- a/neoforge-main/build.gradle.kts +++ b/neoforge-main/build.gradle.kts @@ -62,10 +62,10 @@ neoForge { logLevel.set(org.slf4j.event.Level.DEBUG) sourceSet = project.sourceSets.main - // JetBrains Runtime Hotswap - if (!System.getenv().containsKey("CI")) { - jvmArgument("-XX:+AllowEnhancedClassRedefinition") - } +// // JetBrains Runtime Hotswap +// if (!System.getenv().containsKey("CI")) { +// jvmArgument("-XX:+AllowEnhancedClassRedefinition") +// } } create("client") { @@ -121,6 +121,9 @@ repositories { password = project.findProperty("gpr.token") as String? ?: System.getenv("GITHUB_TOKEN") } } + maven("https://cursemaven.com"){ + name = "CurseMaven" + } } dependencies { @@ -135,6 +138,9 @@ dependencies { testImplementation("org.junit.jupiter:junit-jupiter:5.7.1") testRuntimeOnly("org.junit.platform:junit-platform-launcher") + implementation("curse.maven:jei-238222:7024953") + implementation("curse.maven:the-one-probe-245211:5836106") + jarJar(libs.rxjava) jarJar(libs.reactivestreams) } diff --git a/neoforge-main/gtfo/compat/theoneprobe/providers/FieldProjectorProvider.java b/neoforge-main/gtfo/compat/theoneprobe/providers/FieldProjectorProvider.java deleted file mode 100644 index 9cf45d27..00000000 --- a/neoforge-main/gtfo/compat/theoneprobe/providers/FieldProjectorProvider.java +++ /dev/null @@ -1,100 +0,0 @@ -package dev.compactmods.crafting.compat.theoneprobe.providers; - -import dev.compactmods.crafting.CompactCrafting; -import dev.compactmods.crafting.api.EnumCraftingState; -import dev.compactmods.crafting.api.recipe.IMiniaturizationRecipe; -import dev.compactmods.crafting.core.CCCapabilities; -import dev.compactmods.crafting.projector.FieldProjectorBlock; -import dev.compactmods.crafting.projector.FieldProjectorEntity; -import mcjty.theoneprobe.api.Color; -import mcjty.theoneprobe.api.ElementAlignment; -import mcjty.theoneprobe.api.IProbeHitData; -import mcjty.theoneprobe.api.IProbeInfo; -import mcjty.theoneprobe.api.IProbeInfoProvider; -import mcjty.theoneprobe.api.ProbeMode; -import net.minecraft.network.chat.Component; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.block.state.BlockState; - -import java.util.Set; - -public class FieldProjectorProvider implements IProbeInfoProvider { - - private static final ResourceLocation ID = new ResourceLocation(CompactCrafting.MOD_ID, "field_projector"); - @Override - public ResourceLocation getID() { - return ID; - } - - @Override - public void addProbeInfo(ProbeMode mode, IProbeInfo info, Player player, Level level, BlockState state, IProbeHitData hitData) { - if (!(state.getBlock() instanceof FieldProjectorBlock)) - return; - - // add info from server - if (FieldProjectorBlock.isActive(state) && level.getBlockEntity(hitData.getPos()) instanceof FieldProjectorEntity mFieldEntity) { - mFieldEntity.getCapability(CCCapabilities.MINIATURIZATION_FIELD) - .ifPresent(field -> { - IMiniaturizationRecipe recipe = field.getCurrentRecipe().orElse(null); - - final IProbeInfo recipeProgress = info.vertical( - info.defaultLayoutStyle() - .alignment(ElementAlignment.ALIGN_CENTER) - .spacing(1)); - - IProbeInfo group = recipeProgress.horizontal(info.defaultLayoutStyle() - .alignment(ElementAlignment.ALIGN_CENTER)); - - // group.text(new TranslationTextComponent(CompactCrafting.MOD_ID + ".top.current_recipe")); - - if(recipe != null) { - int progress = field.getProgress(); - - final Set possible = recipe.getCatalyst().getPossible(); - group.item(possible.size() > 1 ? possible.stream().findAny().get() : possible.stream().findFirst().get()); - - group.icon( - new ResourceLocation(CompactCrafting.MOD_ID, "textures/gui/jei-arrow-outputs.png"), - 0, 0, 24, 19, info.defaultIconStyle().textureHeight(19) - .textureWidth(24) - .height(19).width(24)); - - for (ItemStack out : recipe.getOutputs()) { - group.item(out); - } - - recipeProgress - .progress(1, 1, info.defaultProgressStyle() - .showText(false).borderColor(0).backgroundColor(0) - .height(0).width(110).filledColor(0) - .alternateFilledColor(0)) - - .progress(progress, recipe.getCraftingTime(), - info.defaultProgressStyle() - .height(5) - .width(100) - .showText(false) - .filledColor(0xFFCCCCCC) - .alternateFilledColor(0xFFCCCCCC) - .backgroundColor(Color.rgb(255, 250, 250, 50)) - .borderColor(0x00000000)) - ; - - if (field.getCraftingState() == EnumCraftingState.MATCHED && progress == 0) { - recipeProgress - .text(Component.translatable(CompactCrafting.MOD_ID + ".top.awaiting_catalyst")); - - } else { - if(mode == ProbeMode.EXTENDED) { - recipeProgress - .text(Component.translatable(CompactCrafting.MOD_ID + ".top.progress", progress, recipe.getCraftingTime())); - } - } - } - }); - } - } -} diff --git a/neoforge-main/gtfo/fakeworld/RecipeChunk.java b/neoforge-main/gtfo/fakeworld/RecipeChunk.java deleted file mode 100644 index ee32a2b5..00000000 --- a/neoforge-main/gtfo/fakeworld/RecipeChunk.java +++ /dev/null @@ -1,86 +0,0 @@ -//package dev.compactmods.crafting.client.fakeworld; -// -//import dev.compactmods.crafting.api.components.IRecipeBlockComponent; -//import dev.compactmods.crafting.api.recipe.layers.IRecipeLayer; -//import dev.compactmods.crafting.recipes.MiniaturizationRecipe; -//import dev.compactmods.crafting.util.BlockSpaceUtil; -//import net.minecraft.core.BlockPos; -//import net.minecraft.core.registries.Registries; -//import net.minecraft.world.level.ChunkPos; -//import net.minecraft.world.level.biome.Biomes; -//import net.minecraft.world.level.block.Blocks; -//import net.minecraft.world.level.block.EntityBlock; -//import net.minecraft.world.level.block.entity.BlockEntity; -//import net.minecraft.world.level.block.state.BlockState; -//import net.minecraft.world.level.chunk.EmptyLevelChunk; -//import net.minecraft.world.level.material.FluidState; -// -//import org.jetbrains.annotations.Nullable; -//import java.util.HashMap; -//import java.util.Map; -//import java.util.Optional; -// -//public class RecipeChunk extends EmptyLevelChunk { -// private final MiniaturizationRecipe recipe; -// private final Map blockCache; -// private final Map tileCache; -// -// public RecipeChunk(RenderingWorld renderingLevel, ChunkPos chunkPos, MiniaturizationRecipe recipe) { -// super(renderingLevel, chunkPos, renderingLevel.registryAccess().registryOrThrow(Registries.BIOME).getHolderOrThrow(Biomes.THE_VOID)); -// this.recipe = recipe; -// -// this.blockCache = new HashMap<>(); -// this.tileCache = new HashMap<>(); -// -// BlockSpaceUtil.getBlocksIn(recipe.getDimensions()).forEach(pos -> { -// int y = pos.getY(); -// Optional layer = recipe.getLayer(y); -// -// if(!layer.isPresent()) -// return; -// -// IRecipeLayer rLayer = layer.get(); -// Optional componentForPosition = rLayer.getComponentForPosition(pos.below(y)); -// -// BlockState posState = componentForPosition -// .flatMap(recipe.getComponents()::getBlock) -// .map(IRecipeBlockComponent::getRenderState) -// .orElse(Blocks.VOID_AIR.defaultBlockState()); -// -// blockCache.put(pos, posState); -// -// if(posState.getBlock() instanceof EntityBlock eb) { -// BlockEntity tile = eb.newBlockEntity(pos.immutable(), posState); -// if(tile != null) { -// tile.setLevel(renderingLevel); -// tileCache.put(pos.immutable(), tile); -// } -// } -// }); -// } -// -// @Override -// public BlockState getBlockState(BlockPos pos) { -// if(blockCache.containsKey(pos)) -// return blockCache.get(pos); -// -// return Blocks.VOID_AIR.defaultBlockState(); -// } -// -// @Override -// public FluidState getFluidState(BlockPos pos) { -// return getBlockState(pos).getFluidState(); -// } -// -// @Nullable -// @Override -// public BlockEntity getBlockEntity(BlockPos pos) { -// return tileCache.get(pos); -// } -// -// @Nullable -// @Override -// public BlockEntity getBlockEntity(BlockPos pos, EntityCreationType createType) { -// return tileCache.get(pos); -// } -//} diff --git a/neoforge-main/gtfo/fakeworld/RenderingChunkProvider.java b/neoforge-main/gtfo/fakeworld/RenderingChunkProvider.java deleted file mode 100644 index 8170d3f3..00000000 --- a/neoforge-main/gtfo/fakeworld/RenderingChunkProvider.java +++ /dev/null @@ -1,83 +0,0 @@ -//package dev.compactmods.crafting.client.fakeworld; -// -//import java.util.ArrayList; -//import java.util.HashMap; -//import java.util.List; -//import java.util.Map; -//import java.util.function.BooleanSupplier; -//import java.util.stream.Collectors; -//import com.mojang.datafixers.util.Pair; -//import dev.compactmods.crafting.recipes.MiniaturizationRecipe; -//import net.minecraft.core.BlockPos; -//import net.minecraft.core.Holder; -//import net.minecraft.world.level.BlockGetter; -//import net.minecraft.world.level.ChunkPos; -//import net.minecraft.world.level.biome.Biome; -//import net.minecraft.world.level.biome.Biomes; -//import net.minecraft.world.level.chunk.ChunkAccess; -//import net.minecraft.world.level.chunk.ChunkSource; -//import net.minecraft.world.level.chunk.ChunkStatus; -//import net.minecraft.world.level.chunk.EmptyLevelChunk; -//import net.minecraft.world.level.lighting.LevelLightEngine; -//import net.minecraftforge.registries.ForgeRegistries; -//import org.jetbrains.annotations.Nullable; -// -//public class RenderingChunkProvider extends ChunkSource { -// private final Holder VOID; -// private final MiniaturizationRecipe recipe; -// -// private final Map chunks; -// private final RenderingWorld renderingLevel; -// private final LevelLightEngine lightManager; -// -// public RenderingChunkProvider(RenderingWorld renderingLevel, MiniaturizationRecipe recipe) { -// VOID = ForgeRegistries.BIOMES.getHolder(Biomes.THE_VOID).get(); -// -// this.recipe = recipe; -// -// this.renderingLevel = renderingLevel; -// this.lightManager = new LevelLightEngine(this, true, true); -// -// Map> byChunk = new HashMap<>(); -// BlockPos.betweenClosedStream(this.recipe.getDimensions()) -// .map(BlockPos::immutable) -// .forEach(pos -> { -// byChunk.computeIfAbsent(new ChunkPos(pos), $ -> new ArrayList<>()).add(pos); -// }); -// -// chunks = byChunk.keySet().stream() -// .map(chunkPos -> Pair.of(chunkPos, new RecipeChunk(this.renderingLevel, chunkPos, recipe))) -// .collect(Collectors.toMap(Pair::getFirst, Pair::getSecond)); -// } -// -// @Nullable -// @Override -// public ChunkAccess getChunk(int cx, int cz, ChunkStatus status, boolean load) { -// return chunks.computeIfAbsent(new ChunkPos(cx, cz), p -> new EmptyLevelChunk(renderingLevel, p, VOID)); -// } -// -// @Override -// public void tick(BooleanSupplier bool, boolean bool2) { -// -// } -// -// @Override -// public String gatherStats() { -// return "?"; -// } -// -// @Override -// public int getLoadedChunksCount() { -// return chunks.size(); -// } -// -// @Override -// public LevelLightEngine getLightEngine() { -// return lightManager; -// } -// -// @Override -// public BlockGetter getLevel() { -// return renderingLevel; -// } -//} diff --git a/neoforge-main/gtfo/fakeworld/RenderingSpawnInfo.java b/neoforge-main/gtfo/fakeworld/RenderingSpawnInfo.java deleted file mode 100644 index ded6706f..00000000 --- a/neoforge-main/gtfo/fakeworld/RenderingSpawnInfo.java +++ /dev/null @@ -1,116 +0,0 @@ -//package dev.compactmods.crafting.client.fakeworld; -// -//import net.minecraft.world.Difficulty; -//import net.minecraft.world.level.GameRules; -//import net.minecraft.world.level.storage.WritableLevelData; -// -//public class RenderingSpawnInfo implements WritableLevelData { -// private static final GameRules RULES = new GameRules(); -// -// private int spawnX; -// private int spawnY; -// private int spawnZ; -// private float spawnAngle; -// -// @Override -// public void setXSpawn(int x) -// { -// spawnX = x; -// } -// -// @Override -// public void setYSpawn(int y) -// { -// spawnY = y; -// } -// -// @Override -// public void setZSpawn(int z) -// { -// spawnZ = z; -// } -// -// @Override -// public void setSpawnAngle(float angle) -// { -// spawnAngle = angle; -// } -// -// @Override -// public int getXSpawn() -// { -// return spawnX; -// } -// -// @Override -// public int getYSpawn() -// { -// return spawnY; -// } -// -// -// @Override -// public int getZSpawn() -// { -// return spawnZ; -// } -// -// @Override -// public float getSpawnAngle() -// { -// return spawnAngle; -// } -// -// @Override -// public long getGameTime() -// { -// return 0; -// } -// -// @Override -// public long getDayTime() -// { -// return 0; -// } -// -// @Override -// public boolean isThundering() -// { -// return false; -// } -// -// @Override -// public boolean isRaining() -// { -// return false; -// } -// -// @Override -// public void setRaining(boolean isRaining) -// { -// -// } -// -// @Override -// public boolean isHardcore() -// { -// return false; -// } -// -// @Override -// public GameRules getGameRules() { -// return RULES; -// } -// -// @Override -// public Difficulty getDifficulty() -// { -// return Difficulty.PEACEFUL; -// } -// -// @Override -// public boolean isDifficultyLocked() -// { -// return false; -// } -//} diff --git a/neoforge-main/gtfo/fakeworld/RenderingWorld.java b/neoforge-main/gtfo/fakeworld/RenderingWorld.java deleted file mode 100644 index 33a8a2a6..00000000 --- a/neoforge-main/gtfo/fakeworld/RenderingWorld.java +++ /dev/null @@ -1,191 +0,0 @@ -//package dev.compactmods.crafting.client.fakeworld; -// -//import dev.compactmods.crafting.recipes.MiniaturizationRecipe; -//import net.minecraft.client.Minecraft; -//import net.minecraft.core.*; -//import net.minecraft.core.registries.BuiltInRegistries; -//import net.minecraft.core.registries.Registries; -//import net.minecraft.sounds.SoundEvent; -//import net.minecraft.sounds.SoundSource; -//import net.minecraft.util.profiling.InactiveProfiler; -//import net.minecraft.world.TickRateManager; -//import net.minecraft.world.entity.Entity; -//import net.minecraft.world.entity.player.Player; -//import net.minecraft.world.flag.FeatureFlagSet; -//import net.minecraft.world.item.crafting.RecipeManager; -//import net.minecraft.world.level.Level; -//import net.minecraft.world.level.biome.Biome; -//import net.minecraft.world.level.biome.Biomes; -//import net.minecraft.world.level.block.Block; -//import net.minecraft.world.level.block.state.BlockState; -//import net.minecraft.world.level.chunk.ChunkSource; -//import net.minecraft.world.level.dimension.BuiltinDimensionTypes; -//import net.minecraft.world.level.dimension.DimensionType; -//import net.minecraft.world.level.entity.LevelEntityGetter; -//import net.minecraft.world.level.gameevent.GameEvent; -//import net.minecraft.world.level.material.Fluid; -//import net.minecraft.world.level.saveddata.maps.MapItemSavedData; -//import net.minecraft.world.phys.Vec3; -//import net.minecraft.world.scores.Scoreboard; -//import net.minecraft.world.ticks.BlackholeTickAccess; -//import net.minecraft.world.ticks.LevelTickAccess; -// -//import org.jetbrains.annotations.Nullable; -//import java.util.Collections; -//import java.util.List; -// -//public class RenderingWorld extends Level { -// -// private final MiniaturizationRecipe recipe; -// -// private final Scoreboard scoreboard = new Scoreboard(); -// private final RecipeManager recipeManager = new RecipeManager(); -// private final RenderingChunkProvider chunkProvider; -// -// public RenderingWorld(MiniaturizationRecipe recipe) { -// super(new RenderingSpawnInfo(), Level.OVERWORLD, -// Minecraft.getInstance().level.registryAccess(), -// Minecraft.getInstance().level.registryAccess().registryOrThrow(Registries.DIMENSION_TYPE).getHolderOrThrow(BuiltinDimensionTypes.OVERWORLD), -// () -> InactiveProfiler.INSTANCE, true, false, 0, 1000000); -// -// this.recipe = recipe; -// this.chunkProvider = new RenderingChunkProvider(this, recipe); -// } -// -// @Override -// public void sendBlockUpdated(BlockPos p_184138_1_, BlockState p_184138_2_, BlockState p_184138_3_, int p_184138_4_) { -// -// } -// -// @Override -// public void playSeededSound(@Nullable Player p_262953_, double p_263004_, double p_263398_, double p_263376_, Holder p_263359_, SoundSource p_263020_, float p_263055_, float p_262914_, long p_262991_) { -// -// } -// -// @Override -// public void playSeededSound(@org.jetbrains.annotations.Nullable Player p_220363_, double p_220364_, double p_220365_, double p_220366_, SoundEvent p_220367_, SoundSource p_220368_, float p_220369_, float p_220370_, long p_220371_) { -// -// } -// -// @Override -// public void playSeededSound(@Nullable Player p_220372_, Entity p_220373_, Holder p_263500_, SoundSource p_220375_, float p_220376_, float p_220377_, long p_220378_) { -// -// } -// -// @Override -// public void playSound(@Nullable Player p_184148_1_, double p_184148_2_, double p_184148_4_, double p_184148_6_, SoundEvent p_184148_8_, SoundSource p_184148_9_, float p_184148_10_, float p_184148_11_) { -// -// } -// -// @Override -// public void playSound(@Nullable Player p_217384_1_, Entity p_217384_2_, SoundEvent p_217384_3_, SoundSource p_217384_4_, float p_217384_5_, float p_217384_6_) { -// -// } -// -// @Override -// public String gatherChunkSourceStats() { -// return null; -// } -// -// @Nullable -// @Override -// public Entity getEntity(int p_73045_1_) { -// return null; -// } -// -// @Override -// public TickRateManager tickRateManager() { -// return null; -// } -// -// @Nullable -// @Override -// public MapItemSavedData getMapData(String p_217406_1_) { -// return null; -// } -// -// @Override -// public void setMapData(String p_151533_, MapItemSavedData p_151534_) { -// -// } -// -// @Override -// public int getFreeMapId() { -// return 0; -// } -// -// @Override -// public void destroyBlockProgress(int p_175715_1_, BlockPos p_175715_2_, int p_175715_3_) { -// -// } -// -// @Override -// public Scoreboard getScoreboard() { -// return scoreboard; -// } -// -// @Override -// public RecipeManager getRecipeManager() { -// return recipeManager; -// } -// -// @Override -// protected LevelEntityGetter getEntities() { -// return null; -// } -// -// @Override -// public LevelTickAccess getBlockTicks() { -// return BlackholeTickAccess.emptyLevelList(); -// } -// -// @Override -// public LevelTickAccess getFluidTicks() { -// return BlackholeTickAccess.emptyLevelList(); -// } -// -// @Override -// public ChunkSource getChunkSource() { -// return chunkProvider; -// } -// -// @Override -// public void levelEvent(@Nullable Player p_217378_1_, int p_217378_2_, BlockPos p_217378_3_, int p_217378_4_) { -// -// } -// -// @Override -// public void gameEvent(GameEvent p_220404_, Vec3 p_220405_, GameEvent.Context p_220406_) { -// -// } -// -// @Override -// public void gameEvent(@Nullable Entity p_151549_, GameEvent p_151550_, BlockPos p_151551_) { -// -// } -// -// @Override -// public RegistryAccess registryAccess() { -// return Minecraft.getInstance().level.registryAccess(); -// } -// -// @Override -// public FeatureFlagSet enabledFeatures() { -// return null; -// } -// -// @Override -// public float getShade(Direction p_230487_1_, boolean p_230487_2_) { -// return 0; -// } -// -// @Override -// public List players() { -// return Collections.emptyList(); -// } -// -// @Override -// public Holder getUncachedNoiseBiome(int p_225604_1_, int p_225604_2_, int p_225604_3_) { -// // return registryAccess().registryOrThrow(Registry.BIOME_REGISTRY).getHolderOrThrow(Biomes.THE_VOID); -// } -//} diff --git a/neoforge-main/gtfo/proxies/data/BaseFieldProxyEntity.java b/neoforge-main/gtfo/proxies/data/BaseFieldProxyEntity.java deleted file mode 100644 index bbc78dee..00000000 --- a/neoforge-main/gtfo/proxies/data/BaseFieldProxyEntity.java +++ /dev/null @@ -1,45 +0,0 @@ -package dev.compactmods.crafting.proxies.data; - -import net.minecraft.core.BlockPos; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.NbtUtils; -import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraft.world.level.block.entity.BlockEntityType; -import net.minecraft.world.level.block.state.BlockState; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -public abstract class BaseFieldProxyEntity extends BlockEntity { - - @Nullable - protected BlockPos fieldCenter; - - public BaseFieldProxyEntity(BlockEntityType type, BlockPos pos, BlockState state) { - super(type, pos, state); - } - - public void updateField(BlockPos fieldCenter) { - if (level == null) - return; - - this.fieldCenter = null; - setChanged(); - } - - @Override - protected void saveAdditional(@NotNull CompoundTag tag) { - super.saveAdditional(tag); - - if(this.fieldCenter != null) - tag.put("center", NbtUtils.writeBlockPos(this.fieldCenter)); - } - - @Override - public void load(CompoundTag tag) { - super.load(tag); - - if(tag.contains("center")) { - this.fieldCenter = NbtUtils.readBlockPos(tag.getCompound("center")); - } - } -} diff --git a/neoforge-main/gtfo/proxies/data/MatchFieldProxyEntity.java b/neoforge-main/gtfo/proxies/data/MatchFieldProxyEntity.java deleted file mode 100644 index bc72f612..00000000 --- a/neoforge-main/gtfo/proxies/data/MatchFieldProxyEntity.java +++ /dev/null @@ -1,13 +0,0 @@ -package dev.compactmods.crafting.proxies.data; - -import dev.compactmods.crafting.core.CCBlocks; -import net.minecraft.core.BlockPos; -import net.minecraft.world.level.block.state.BlockState; - -public class MatchFieldProxyEntity extends BaseFieldProxyEntity { - - public MatchFieldProxyEntity(BlockPos pos, BlockState state) { - super(CCBlocks.MATCH_PROXY_ENTITY.get(), pos, state); - } - -} diff --git a/neoforge-main/src/main/java/dev/compactmods/crafting/CompactCrafting.java b/neoforge-main/src/main/java/dev/compactmods/crafting/CompactCrafting.java index ca664cdd..487222f1 100644 --- a/neoforge-main/src/main/java/dev/compactmods/crafting/CompactCrafting.java +++ b/neoforge-main/src/main/java/dev/compactmods/crafting/CompactCrafting.java @@ -2,6 +2,7 @@ import dev.compactmods.crafting.client.ClientConfig; import dev.compactmods.crafting.core.CCBlocks; +import dev.compactmods.crafting.core.CCDataComponents; import dev.compactmods.crafting.core.CCItems; import dev.compactmods.crafting.core.CCLayerTypes; import dev.compactmods.crafting.core.CCMiniaturizationRecipes; @@ -32,6 +33,7 @@ public CompactCrafting(IEventBus modBus, ModContainer modContainer) { CCBlocks.init(modBus); CCItems.init(modBus); + CCDataComponents.init(modBus); CCLayerTypes.init(modBus); CCMiniaturizationRecipes.init(modBus); ComponentRegistration.init(modBus); diff --git a/neoforge-main/src/main/java/dev/compactmods/crafting/client/ClientEventHandler.java b/neoforge-main/src/main/java/dev/compactmods/crafting/client/ClientEventHandler.java index d003e478..4a910ce3 100644 --- a/neoforge-main/src/main/java/dev/compactmods/crafting/client/ClientEventHandler.java +++ b/neoforge-main/src/main/java/dev/compactmods/crafting/client/ClientEventHandler.java @@ -2,16 +2,27 @@ import dev.compactmods.crafting.CompactCrafting; import dev.compactmods.crafting.client.render.GhostProjectorPlacementRenderer; +import dev.compactmods.crafting.client.render.ProxyProjectorHighlighter; +import dev.compactmods.crafting.core.CCBlocks; +import dev.compactmods.crafting.data.CCAttachments; +import dev.compactmods.crafting.proxies.data.BaseFieldProxyEntity; import net.minecraft.client.Camera; import net.minecraft.client.Minecraft; import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.core.BlockPos; +import net.minecraft.network.chat.Component; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.phys.BlockHitResult; import net.minecraft.world.phys.HitResult; import net.neoforged.api.distmarker.Dist; import net.neoforged.bus.api.SubscribeEvent; import net.neoforged.fml.common.EventBusSubscriber; import net.neoforged.neoforge.client.event.ClientTickEvent; +import net.neoforged.neoforge.client.event.RenderHighlightEvent; import net.neoforged.neoforge.client.event.RenderLevelStageEvent; +import net.neoforged.neoforge.event.entity.player.PlayerInteractEvent; import javax.annotation.Nonnull; @@ -21,6 +32,7 @@ public class ClientEventHandler { @SubscribeEvent public static void onTick(final ClientTickEvent.Post evt) { GhostProjectorPlacementRenderer.tick(); + ProxyProjectorHighlighter.cleanupExpiredBlinking(); ClientLevel level = Minecraft.getInstance().level; if (level != null && !Minecraft.getInstance().isPaused()) { @@ -39,6 +51,13 @@ public static void onWorldRender(final RenderLevelStageEvent event) { GhostProjectorPlacementRenderer.render(event.getPoseStack()); doFieldPreviewRender(event, mc); } + + if (event.getStage().equals(RenderLevelStageEvent.Stage.AFTER_TRANSLUCENT_BLOCKS)) { + final MultiBufferSource.BufferSource buffers = mc.renderBuffers().bufferSource(); + ProxyProjectorHighlighter.renderAllBlinkingProjectors(event.getPoseStack(), buffers, mc.level); + buffers.endBatch(); + } + } @Nonnull @@ -82,4 +101,43 @@ private static void doFieldPreviewRender(RenderLevelStageEvent event, Minecraft // }); buffers.endBatch(); } + + @SubscribeEvent + public static void onRightClickBlock(final PlayerInteractEvent.RightClickBlock event) { + if (!event.getLevel().isClientSide) return; + if (event.getHand() != InteractionHand.MAIN_HAND) return; + + BlockPos pos = event.getPos(); + Block block = event.getLevel().getBlockState(pos).getBlock(); + + if (block == CCBlocks.MATCH_FIELD_PROXY_BLOCK.get() || block == CCBlocks.RESCAN_FIELD_PROXY_BLOCK.get()) { + BlockPos fieldCenter = ClientPacketHandler.getProxyFieldCenter(pos); + if (fieldCenter == null) { + event.getEntity().displayClientMessage(Component.literal("Proxy is not bound to any fields !"), true); + return; + } + + var fields = event.getLevel().getData(CCAttachments.ACTIVE_FIELDS); + fields.get(fieldCenter).ifPresent(field -> { + boolean anyAdded = false; + int alreadyBlinking = 0; + + for (BlockPos projectorPos : field.getProjectors().locations()) { + if (ProxyProjectorHighlighter.addBlinkingProjector(projectorPos)) { + anyAdded = true; + } else { + alreadyBlinking++; + } + } + + if (anyAdded && alreadyBlinking == 0) { + event.getEntity().sendSystemMessage(Component.literal("Projectors highlighted for 10 seconds")); + } else if (alreadyBlinking > 0 && !anyAdded) { + event.getEntity().sendSystemMessage(Component.literal("Projectors are already highlighted")); + } else if (anyAdded && alreadyBlinking > 0) { + event.getEntity().sendSystemMessage(Component.literal("Some projectors highlighted for 10 seconds (others already active)")); + } + }); + } + } } diff --git a/neoforge-main/src/main/java/dev/compactmods/crafting/client/ClientPacketHandler.java b/neoforge-main/src/main/java/dev/compactmods/crafting/client/ClientPacketHandler.java index b2aa98cc..96d092dd 100644 --- a/neoforge-main/src/main/java/dev/compactmods/crafting/client/ClientPacketHandler.java +++ b/neoforge-main/src/main/java/dev/compactmods/crafting/client/ClientPacketHandler.java @@ -6,17 +6,24 @@ import dev.compactmods.crafting.field.IMutableMiniaturizationField; import dev.compactmods.crafting.field.MiniaturizationField; import dev.compactmods.crafting.network.FieldActivatedPacket; -import dev.compactmods.crafting.projector.FieldProjectorBlock; import dev.compactmods.crafting.recipes.MiniaturizationRecipe; import net.minecraft.client.Minecraft; import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.core.BlockPos; import net.minecraft.nbt.CompoundTag; import net.minecraft.world.item.crafting.RecipeHolder; +import org.jetbrains.annotations.Nullable; -import java.util.stream.Stream; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; public abstract class ClientPacketHandler { + + private static final Map clientProxyDataMap = new ConcurrentHashMap<>(); + private static final long CACHE_DURATION = 1000; + + private static record ProxyData(@Nullable BlockPos fieldCenter, UUID proxyId, long timestamp) {} public static void handleFieldActivation(IMiniaturizationField field, CompoundTag fieldClientData) { Minecraft mc = Minecraft.getInstance(); @@ -25,8 +32,6 @@ public static void handleFieldActivation(IMiniaturizationField { -//// fields.setLevel(mc.level); -//// CompactCrafting.LOGGER.debug("Registering field on client"); -//// final IMiniaturizationField fieldRegistered = fields.registerField(field); -//// -//// CompactCrafting.LOGGER.debug("Setting field references"); -//// -//// field.getProjectorPositions() -//// .map(mc.level::getBlockEntity) -//// .map(tile -> (FieldProjectorEntity) tile) -//// .filter(Objects::nonNull) -//// .forEach(tile -> { -//// final BlockState state = tile.getBlockState(); -//// tile.setFieldRef(fieldRegistered.getRef()); -//// }); -//// }); -// } + public static void handleFieldData(CompoundTag fieldData) { + Minecraft mc = Minecraft.getInstance(); + if (mc.level == null) + return; + + MiniaturizationField field = MiniaturizationField.fromNBT(mc.level, fieldData); + mc.level.getData(CCAttachments.ACTIVE_FIELDS).registerField(field); + } public static void removeField(BlockPos fieldCenter) { Minecraft mc = Minecraft.getInstance(); @@ -112,7 +98,33 @@ public static void changeFieldRecipe(BlockPos center, RecipeHolder CACHE_DURATION; + } + + public static void removeProxyData(BlockPos proxyPos) { + clientProxyDataMap.remove(proxyPos); + } + + public static void validateAndGetProxyData(BlockPos proxyPos, UUID currentProxyId) { + ProxyData data = clientProxyDataMap.get(proxyPos); + if (data != null && !data.proxyId.equals(currentProxyId)) { + clientProxyDataMap.remove(proxyPos); + } + } } diff --git a/neoforge-main/src/main/java/dev/compactmods/crafting/client/CompactCraftingClient.java b/neoforge-main/src/main/java/dev/compactmods/crafting/client/CompactCraftingClient.java index 58e3a7b6..f20c9434 100644 --- a/neoforge-main/src/main/java/dev/compactmods/crafting/client/CompactCraftingClient.java +++ b/neoforge-main/src/main/java/dev/compactmods/crafting/client/CompactCraftingClient.java @@ -2,14 +2,17 @@ import dev.compactmods.crafting.CompactCrafting; import dev.compactmods.crafting.client.render.field.MiniaturizationFieldRenderer; +import net.neoforged.api.distmarker.Dist; import net.neoforged.bus.api.IEventBus; import net.neoforged.fml.common.Mod; +import net.neoforged.neoforge.client.event.RenderHighlightEvent; import net.neoforged.neoforge.common.NeoForge; -@Mod(CompactCrafting.MOD_ID) +@Mod(value = CompactCrafting.MOD_ID, dist = Dist.CLIENT) public class CompactCraftingClient { public CompactCraftingClient(IEventBus modBus) { NeoForge.EVENT_BUS.addListener(MiniaturizationFieldRenderer::onRenderStage); } + } diff --git a/neoforge-main/src/main/java/dev/compactmods/crafting/client/render/CCRenderTypes.java b/neoforge-main/src/main/java/dev/compactmods/crafting/client/render/CCRenderTypes.java index 36c282b9..e73515f8 100644 --- a/neoforge-main/src/main/java/dev/compactmods/crafting/client/render/CCRenderTypes.java +++ b/neoforge-main/src/main/java/dev/compactmods/crafting/client/render/CCRenderTypes.java @@ -6,6 +6,8 @@ import net.minecraft.client.renderer.RenderStateShard; import net.minecraft.client.renderer.RenderType; +import java.util.OptionalDouble; + public class CCRenderTypes { protected static final RenderStateShard.ShaderStateShard BLOCK_SHADER = @@ -28,5 +30,18 @@ public class CCRenderTypes { .setLightmapState(RenderStateShard.LIGHTMAP) .setTextureState(RenderStateShard.BLOCK_SHEET_MIPPED) .setTransparencyState(RenderStateShard.TRANSLUCENT_TRANSPARENCY) + .setDepthTestState(RenderStateShard.LEQUAL_DEPTH_TEST) + .setCullState(RenderStateShard.NO_CULL) + .setWriteMaskState(RenderStateShard.COLOR_DEPTH_WRITE) .createCompositeState(true)); + + public static final RenderType PROJECTOR_HIGHLIGHT = RenderType.create("projector_highlight", + DefaultVertexFormat.POSITION_COLOR, VertexFormat.Mode.QUADS, 256, + RenderType.CompositeState.builder() + .setShaderState(RenderStateShard.POSITION_COLOR_SHADER) + .setTransparencyState(RenderStateShard.TRANSLUCENT_TRANSPARENCY) + .setDepthTestState(RenderStateShard.NO_DEPTH_TEST) + .setCullState(RenderStateShard.NO_CULL) + .setWriteMaskState(RenderStateShard.COLOR_WRITE) + .createCompositeState(false)); } diff --git a/neoforge-main/src/main/java/dev/compactmods/crafting/client/render/ProxyProjectorHighlighter.java b/neoforge-main/src/main/java/dev/compactmods/crafting/client/render/ProxyProjectorHighlighter.java new file mode 100644 index 00000000..15b4e039 --- /dev/null +++ b/neoforge-main/src/main/java/dev/compactmods/crafting/client/render/ProxyProjectorHighlighter.java @@ -0,0 +1,196 @@ +package dev.compactmods.crafting.client.render; + +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.vertex.VertexConsumer; +import dev.compactmods.crafting.client.render.CCRenderTypes; +import dev.compactmods.crafting.client.ClientPacketHandler; +import dev.compactmods.crafting.data.CCAttachments; +import dev.compactmods.crafting.network.RequestProxyDataPacket; +import dev.compactmods.crafting.proxies.data.BaseFieldProxyEntity; +import net.neoforged.neoforge.network.PacketDistributor; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.LevelRenderer; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.world.level.Level; +import net.minecraft.world.phys.AABB; +import net.minecraft.world.phys.Vec3; +import org.joml.Matrix4f; + +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +public class ProxyProjectorHighlighter { + private static final Map blinkingProjectors = new ConcurrentHashMap<>(); + private static final long BLINK_DURATION = 10000; + + public static boolean addBlinkingProjector(BlockPos projectorPos) { + long currentTime = System.currentTimeMillis(); + Long existingTime = blinkingProjectors.get(projectorPos); + + if (existingTime != null && (currentTime - existingTime) < BLINK_DURATION) { + return false; + } + + blinkingProjectors.put(projectorPos, currentTime); + return true; + } + + public static void renderProjectorHighlight(PoseStack poseStack, MultiBufferSource buffers, BlockPos proxyPos, Level level) { + if (level == null) return; + + var blockEntity = level.getBlockEntity(proxyPos); + if (blockEntity instanceof BaseFieldProxyEntity proxy) { + ClientPacketHandler.validateAndGetProxyData(proxyPos, proxy.getProxyId()); + } + + if (ClientPacketHandler.isProxyDataStale(proxyPos)) { + PacketDistributor.sendToServer(new RequestProxyDataPacket(proxyPos)); + } + + BlockPos fieldCenter = ClientPacketHandler.getProxyFieldCenter(proxyPos); + if (fieldCenter == null) { + return; + } + + var fields = level.getData(CCAttachments.ACTIVE_FIELDS); + var fieldOpt = fields.get(fieldCenter); + + if (fieldOpt.isEmpty()) { + return; + } + + fieldOpt.ifPresent(field -> { + Set projectorPositions = new HashSet<>(field.getProjectors().locations()); + + long currentTime = System.currentTimeMillis(); + + for (BlockPos projectorPos : projectorPositions) { + boolean shouldBlink = blinkingProjectors.containsKey(projectorPos); + Long blinkStartTime = blinkingProjectors.get(projectorPos); + + if (shouldBlink && blinkStartTime != null && (currentTime - blinkStartTime) > BLINK_DURATION) { + blinkingProjectors.remove(projectorPos); + shouldBlink = false; + } + + float alpha = 0.3f; + if (shouldBlink && blinkStartTime != null) { + float blinkProgress = (currentTime - blinkStartTime) / 500.0f; + alpha = 0.2f + 0.4f * (float) Math.abs(Math.sin(blinkProgress)); + } + + renderProjectorOutline(poseStack, buffers, projectorPos, alpha); + } + }); + } + + private static void renderProjectorOutline(PoseStack poseStack, MultiBufferSource buffers, BlockPos projectorPos, float alpha) { + Minecraft mc = Minecraft.getInstance(); + if (mc.level == null) return; + + Vec3 cameraPos = mc.gameRenderer.getMainCamera().getPosition(); + + poseStack.pushPose(); + poseStack.translate( + projectorPos.getX() - cameraPos.x, + projectorPos.getY() - cameraPos.y, + projectorPos.getZ() - cameraPos.z + ); + + VertexConsumer consumer = buffers.getBuffer(CCRenderTypes.PROJECTOR_HIGHLIGHT); + Matrix4f matrix = poseStack.last().pose(); + + float red = 1.0f, green = 0.0f, blue = 0.0f; + + renderCubeFaces(consumer, matrix, red, green, blue, alpha); + + poseStack.popPose(); + } + + private static void renderCubeFaces(VertexConsumer consumer, Matrix4f matrix, float red, float green, float blue, float alpha) { + float minX = -0.01f, minY = -0.01f, minZ = -0.01f; + float maxX = 1.01f, maxY = 1.01f, maxZ = 1.01f; + + consumer.addVertex(matrix, minX, minY, minZ).setColor(red, green, blue, alpha); + consumer.addVertex(matrix, maxX, minY, minZ).setColor(red, green, blue, alpha); + consumer.addVertex(matrix, maxX, maxY, minZ).setColor(red, green, blue, alpha); + consumer.addVertex(matrix, minX, maxY, minZ).setColor(red, green, blue, alpha); + + consumer.addVertex(matrix, minX, minY, maxZ).setColor(red, green, blue, alpha); + consumer.addVertex(matrix, minX, maxY, maxZ).setColor(red, green, blue, alpha); + consumer.addVertex(matrix, maxX, maxY, maxZ).setColor(red, green, blue, alpha); + consumer.addVertex(matrix, maxX, minY, maxZ).setColor(red, green, blue, alpha); + + consumer.addVertex(matrix, minX, minY, minZ).setColor(red, green, blue, alpha); + consumer.addVertex(matrix, minX, maxY, minZ).setColor(red, green, blue, alpha); + consumer.addVertex(matrix, minX, maxY, maxZ).setColor(red, green, blue, alpha); + consumer.addVertex(matrix, minX, minY, maxZ).setColor(red, green, blue, alpha); + + consumer.addVertex(matrix, maxX, minY, minZ).setColor(red, green, blue, alpha); + consumer.addVertex(matrix, maxX, minY, maxZ).setColor(red, green, blue, alpha); + consumer.addVertex(matrix, maxX, maxY, maxZ).setColor(red, green, blue, alpha); + consumer.addVertex(matrix, maxX, maxY, minZ).setColor(red, green, blue, alpha); + + consumer.addVertex(matrix, minX, minY, minZ).setColor(red, green, blue, alpha); + consumer.addVertex(matrix, minX, minY, maxZ).setColor(red, green, blue, alpha); + consumer.addVertex(matrix, maxX, minY, maxZ).setColor(red, green, blue, alpha); + consumer.addVertex(matrix, maxX, minY, minZ).setColor(red, green, blue, alpha); + + consumer.addVertex(matrix, minX, maxY, minZ).setColor(red, green, blue, alpha); + consumer.addVertex(matrix, maxX, maxY, minZ).setColor(red, green, blue, alpha); + consumer.addVertex(matrix, maxX, maxY, maxZ).setColor(red, green, blue, alpha); + consumer.addVertex(matrix, minX, maxY, maxZ).setColor(red, green, blue, alpha); + } + + public static void renderAllBlinkingProjectors(PoseStack poseStack, MultiBufferSource buffers, Level level) { + if (level == null || blinkingProjectors.isEmpty()) return; + + long currentTime = System.currentTimeMillis(); + + for (var entry : blinkingProjectors.entrySet()) { + BlockPos projectorPos = entry.getKey(); + Long blinkStartTime = entry.getValue(); + + if ((currentTime - blinkStartTime) <= BLINK_DURATION) { + float blinkProgress = (currentTime - blinkStartTime) / 500.0f; + float alpha = 0.2f + 0.4f * (float) Math.abs(Math.sin(blinkProgress)); + renderProjectorCube(poseStack, buffers, projectorPos, alpha); + } + } + } + + private static void renderProjectorCube(PoseStack poseStack, MultiBufferSource buffers, BlockPos projectorPos, float alpha) { + Minecraft mc = Minecraft.getInstance(); + if (mc.level == null) return; + + Vec3 cameraPos = mc.gameRenderer.getMainCamera().getPosition(); + + poseStack.pushPose(); + poseStack.translate( + projectorPos.getX() - cameraPos.x, + projectorPos.getY() - cameraPos.y, + projectorPos.getZ() - cameraPos.z + ); + + VertexConsumer consumer = buffers.getBuffer(CCRenderTypes.PROJECTOR_HIGHLIGHT); + Matrix4f matrix = poseStack.last().pose(); + + float red = 1.0f, green = 0.0f, blue = 0.0f; + + renderCubeFaces(consumer, matrix, red, green, blue, alpha); + + poseStack.popPose(); + } + + public static void cleanupExpiredBlinking() { + long currentTime = System.currentTimeMillis(); + blinkingProjectors.entrySet().removeIf(entry -> + (currentTime - entry.getValue()) > BLINK_DURATION); + } + +} \ No newline at end of file diff --git a/neoforge-main/src/main/java/dev/compactmods/crafting/client/render/field/MiniaturizationFieldRenderer.java b/neoforge-main/src/main/java/dev/compactmods/crafting/client/render/field/MiniaturizationFieldRenderer.java index da3273f7..ae0975a0 100644 --- a/neoforge-main/src/main/java/dev/compactmods/crafting/client/render/field/MiniaturizationFieldRenderer.java +++ b/neoforge-main/src/main/java/dev/compactmods/crafting/client/render/field/MiniaturizationFieldRenderer.java @@ -63,8 +63,11 @@ public static void render(Level level, IMiniaturizationField= x && mouseX < x + width && mouseY >= y && mouseY < y + height; + } + + public int getRight() { + return x + width; + } + + public int getBottom() { + return y + height; + } +} \ No newline at end of file diff --git a/neoforge-main/gtfo/compat/ModBusEvents.java b/neoforge-main/src/main/java/dev/compactmods/crafting/compat/ModBusEvents.java similarity index 55% rename from neoforge-main/gtfo/compat/ModBusEvents.java rename to neoforge-main/src/main/java/dev/compactmods/crafting/compat/ModBusEvents.java index 80c1b4c4..83224e11 100644 --- a/neoforge-main/gtfo/compat/ModBusEvents.java +++ b/neoforge-main/src/main/java/dev/compactmods/crafting/compat/ModBusEvents.java @@ -1,13 +1,13 @@ -package dev.compactmods.crafting.events; +package dev.compactmods.crafting.compat; import dev.compactmods.crafting.CompactCrafting; import dev.compactmods.crafting.compat.theoneprobe.TheOneProbeCompat; -import net.minecraftforge.eventbus.api.SubscribeEvent; -import net.minecraftforge.fml.ModList; -import net.minecraftforge.fml.common.Mod; -import net.minecraftforge.fml.event.lifecycle.InterModEnqueueEvent; +import net.neoforged.bus.api.SubscribeEvent; +import net.neoforged.fml.ModList; +import net.neoforged.fml.common.EventBusSubscriber; +import net.neoforged.fml.event.lifecycle.InterModEnqueueEvent; -@Mod.EventBusSubscriber(modid = CompactCrafting.MOD_ID, bus = Mod.EventBusSubscriber.Bus.MOD) +@EventBusSubscriber(modid = CompactCrafting.MOD_ID, bus = EventBusSubscriber.Bus.MOD) public class ModBusEvents { @SubscribeEvent public static void enqueueIMC(final InterModEnqueueEvent event) { diff --git a/neoforge-main/gtfo/compat/jei/JeiMiniaturizationCraftingCategory.java b/neoforge-main/src/main/java/dev/compactmods/crafting/compat/jei/JeiMiniaturizationCraftingCategory.java similarity index 77% rename from neoforge-main/gtfo/compat/jei/JeiMiniaturizationCraftingCategory.java rename to neoforge-main/src/main/java/dev/compactmods/crafting/compat/jei/JeiMiniaturizationCraftingCategory.java index 248a4176..72a3245e 100644 --- a/neoforge-main/gtfo/compat/jei/JeiMiniaturizationCraftingCategory.java +++ b/neoforge-main/src/main/java/dev/compactmods/crafting/compat/jei/JeiMiniaturizationCraftingCategory.java @@ -9,20 +9,19 @@ import com.mojang.blaze3d.platform.Window; import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.vertex.PoseStack; -import com.mojang.math.Matrix4f; -import com.mojang.math.Quaternion; import dev.compactmods.crafting.CompactCrafting; import dev.compactmods.crafting.api.components.IRecipeBlockComponent; import dev.compactmods.crafting.api.recipe.layers.IRecipeLayer; -import dev.compactmods.crafting.client.fakeworld.RenderingWorld; import dev.compactmods.crafting.client.ui.ScreenArea; import dev.compactmods.crafting.core.CCBlocks; +import dev.compactmods.crafting.fakeworld.RenderingWorld; import dev.compactmods.crafting.recipes.MiniaturizationRecipe; import dev.compactmods.crafting.recipes.components.BlockComponent; import dev.compactmods.crafting.util.BlockSpaceUtil; import mezz.jei.api.constants.VanillaTypes; import mezz.jei.api.gui.builder.IRecipeLayoutBuilder; import mezz.jei.api.gui.builder.IRecipeSlotBuilder; +import mezz.jei.api.gui.builder.ITooltipBuilder; import mezz.jei.api.gui.drawable.IDrawable; import mezz.jei.api.gui.drawable.IDrawableStatic; import mezz.jei.api.gui.ingredient.IRecipeSlotsView; @@ -33,8 +32,7 @@ import mezz.jei.api.recipe.category.IRecipeCategory; import net.minecraft.ChatFormatting; import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.GuiComponent; -import net.minecraft.client.renderer.GameRenderer; +import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.renderer.LightTexture; import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.client.renderer.block.BlockRenderDispatcher; @@ -53,12 +51,14 @@ import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.Vec3; -import net.minecraftforge.client.model.data.ModelData; import org.lwjgl.BufferUtils; +import org.joml.Matrix4f; +import org.joml.Quaternionf; +import net.neoforged.neoforge.client.model.data.ModelData; public class JeiMiniaturizationCraftingCategory implements IRecipeCategory { - public static final ResourceLocation UID = new ResourceLocation(CompactCrafting.MOD_ID, "miniaturization"); + public static final ResourceLocation UID = CompactCrafting.modRL("miniaturization"); public static final RecipeType RECIPE_TYPE = new RecipeType<>(UID, MiniaturizationRecipe.class); private final IDrawable icon; @@ -105,7 +105,7 @@ public JeiMiniaturizationCraftingCategory(IGuiHelper guiHelper) { this.background = guiHelper.createBlankDrawable(width, height); this.slotDrawable = guiHelper.getSlotDrawable(); this.icon = guiHelper.createDrawableIngredient(VanillaTypes.ITEM_STACK, new ItemStack(CCBlocks.FIELD_PROJECTOR_BLOCK.get())); - this.arrowOutputs = guiHelper.createDrawable(new ResourceLocation(CompactCrafting.MOD_ID, "textures/gui/jei-arrow-outputs.png"), 0, 0, 24, 19); + this.arrowOutputs = guiHelper.createDrawable(CompactCrafting.modRL("textures/gui/jei-arrow-outputs.png"), 0, 0, 24, 19); this.blocks = Minecraft.getInstance().getBlockRenderer(); this.previewLevel = null; @@ -148,7 +148,6 @@ public void setRecipe(IRecipeLayoutBuilder layout, MiniaturizationRecipe recipe, int fromRightEdge = this.background.getWidth() - (18 * 2) - 5; addOutputSlots(recipe, layout, fromRightEdge); } catch (Exception ex) { - CompactCrafting.LOGGER.error(recipe.getRecipeIdentifier()); CompactCrafting.LOGGER.error("Error displaying recipe", ex); } } @@ -157,14 +156,15 @@ private IRecipeSlotBuilder addCatalystSlots(MiniaturizationRecipe recipe, IRecip final var catalystSlot = layout.addSlot(RecipeIngredientRole.CATALYST, 1, 1) .setBackground(slotDrawable, -1, -1); - if (!recipe.getCatalyst().matches(ItemStack.EMPTY)) { - catalystSlot.addItemStacks(new ArrayList<>(recipe.getCatalyst().getPossible())) - .addTooltipCallback((slots, c) -> c.add(CATALYST)); + ItemStack catalyst = recipe.catalyst(); + if (!catalyst.isEmpty()) { + catalystSlot.addItemStack(catalyst).addTooltipCallback((slots, c) -> c.add(CATALYST)); } return catalystSlot; } + private void addMaterialSlots(MiniaturizationRecipe recipe, IRecipeLayoutBuilder layout) { AtomicInteger inputOffset = new AtomicInteger(); @@ -224,28 +224,26 @@ private void addOutputSlots(MiniaturizationRecipe recipe, IRecipeLayoutBuilder l @Override - public List getTooltipStrings(MiniaturizationRecipe recipe, IRecipeSlotsView slots, double mouseX, double mouseY) { - if (explodeToggle.contains(mouseX, mouseY)) { - if (!exploded) return List.of(Component.translatable("compactcrafting.jei.toggle_exploded_view")); - else return List.of(Component.translatable("compactcrafting.jei.toggle_condensed_view")); + public void getTooltip(ITooltipBuilder tooltip, MiniaturizationRecipe recipe, IRecipeSlotsView slots, double mouseX, double mouseY) { + if (explodeToggle.contains((int)mouseX, (int)mouseY)) { + if (!exploded) tooltip.add(Component.translatable("compactcrafting.jei.toggle_exploded_view")); + else tooltip.add(Component.translatable("compactcrafting.jei.toggle_condensed_view")); } - if (layerSwap.contains(mouseX, mouseY)) { - if (singleLayer) return List.of(Component.translatable("compactcrafting.jei.all_layers_mode")); - else return List.of(Component.translatable("compactcrafting.jei.single_layer_mode")); + if (layerSwap.contains((int)mouseX, (int)mouseY)) { + if (singleLayer) tooltip.add(Component.translatable("compactcrafting.jei.all_layers_mode")); + else tooltip.add(Component.translatable("compactcrafting.jei.single_layer_mode")); } - if (layerUp.contains(mouseX, mouseY) && singleLayer) { + if (layerUp.contains((int)mouseX, (int)mouseY) && singleLayer) { if (singleLayerOffset < recipe.getDimensions().getYsize() - 1) - return List.of(Component.translatable("compactcrafting.jei.layer_up")); + tooltip.add(Component.translatable("compactcrafting.jei.layer_up")); } - if (layerDown.contains(mouseX, mouseY) && singleLayer) { + if (layerDown.contains((int)mouseX, (int)mouseY) && singleLayer) { if (singleLayerOffset > 0) - return List.of(Component.translatable("compactcrafting.jei.layer_down")); + tooltip.add(Component.translatable("compactcrafting.jei.layer_down")); } - - return Collections.emptyList(); } @Override @@ -253,20 +251,20 @@ public boolean handleInput(MiniaturizationRecipe recipe, double mouseX, double m if (input.getType() == InputConstants.Type.MOUSE && input.getValue() == 0) { SoundManager handler = Minecraft.getInstance().getSoundManager(); - if (explodeToggle.contains(mouseX, mouseY)) { + if (explodeToggle.contains((int)mouseX, (int)mouseY)) { explodeMulti = exploded ? 1.0d : 1.6d; exploded = !exploded; handler.play(SimpleSoundInstance.forUI(SoundEvents.UI_BUTTON_CLICK, 1.0F)); return true; } - if (layerSwap.contains(mouseX, mouseY)) { + if (layerSwap.contains((int)mouseX, (int)mouseY)) { singleLayer = !singleLayer; handler.play(SimpleSoundInstance.forUI(SoundEvents.UI_BUTTON_CLICK, 1.0F)); return true; } - if (layerUp.contains(mouseX, mouseY) && singleLayer) { + if (layerUp.contains((int)mouseX, (int)mouseY) && singleLayer) { if (singleLayerOffset < recipe.getDimensions().getYsize() - 1) { handler.play(SimpleSoundInstance.forUI(SoundEvents.UI_BUTTON_CLICK, 1.0F)); singleLayerOffset++; @@ -274,7 +272,7 @@ public boolean handleInput(MiniaturizationRecipe recipe, double mouseX, double m return true; } - if (layerDown.contains(mouseX, mouseY) && singleLayer) { + if (layerDown.contains((int)mouseX, (int)mouseY) && singleLayer) { if (singleLayerOffset > 0) { handler.play(SimpleSoundInstance.forUI(SoundEvents.UI_BUTTON_CLICK, 1.0F)); singleLayerOffset--; @@ -289,36 +287,32 @@ public boolean handleInput(MiniaturizationRecipe recipe, double mouseX, double m //region Rendering help private void drawScaledTexture( - PoseStack matrixStack, + GuiGraphics guiGraphics, ResourceLocation texture, ScreenArea area, float u, float v, int uWidth, int vHeight, int textureWidth, int textureHeight) { - RenderSystem.setShader(GameRenderer::getPositionTexShader); - RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F); - RenderSystem.setShaderTexture(0, texture); - - RenderSystem.enableDepthTest(); - GuiComponent.blit(matrixStack, area.x, area.y, area.width, area.height, u, v, uWidth, vHeight, textureWidth, textureHeight); + guiGraphics.blit(texture, area.x, area.y, area.width, area.height, u, v, uWidth, vHeight, textureWidth, textureHeight); } //endregion @Override - public void draw(MiniaturizationRecipe recipe, IRecipeSlotsView slots, PoseStack pose, double mouseX, double mouseY) { + public void draw(MiniaturizationRecipe recipe, IRecipeSlotsView slots, GuiGraphics guiGraphics, double mouseX, double mouseY) { + PoseStack pose = guiGraphics.pose(); AABB dims = recipe.getDimensions(); Window mainWindow = Minecraft.getInstance().getWindow(); - drawScaledTexture(pose, - new ResourceLocation(CompactCrafting.MOD_ID, "textures/gui/jei-arrow-field.png"), + drawScaledTexture(guiGraphics, + CompactCrafting.modRL("textures/gui/jei-arrow-field.png"), new ScreenArea(7, 20, 17, 22), 0, 0, 17, 22, 17, 22); - drawScaledTexture(pose, - new ResourceLocation(CompactCrafting.MOD_ID, "textures/gui/jei-arrow-outputs.png"), + drawScaledTexture(guiGraphics, + CompactCrafting.modRL("textures/gui/jei-arrow-outputs.png"), new ScreenArea(100, 25, 24, 19), 0, 0, 24, 19, 24, 19); @@ -332,15 +326,15 @@ public void draw(MiniaturizationRecipe recipe, IRecipeSlotsView slots, PoseStack 70 ); - renderPreviewControls(pose, dims); + renderPreviewControls(guiGraphics, dims); - if (previewLevel != null) renderRecipe(recipe, pose, dims, guiScaleFactor, scissorBounds); + if (previewLevel != null) renderRecipe(recipe, guiGraphics, dims, guiScaleFactor, scissorBounds); } - private void renderRecipe(MiniaturizationRecipe recipe, PoseStack mx, AABB dims, double guiScaleFactor, ScreenArea scissorBounds) { + private void renderRecipe(MiniaturizationRecipe recipe, GuiGraphics guiGraphics, AABB dims, double guiScaleFactor, ScreenArea scissorBounds) { + PoseStack mx = guiGraphics.pose(); try { - GuiComponent.fill( - mx, + guiGraphics.fill( scissorBounds.x, scissorBounds.y, scissorBounds.x + scissorBounds.width, scissorBounds.height, @@ -352,7 +346,7 @@ private void renderRecipe(MiniaturizationRecipe recipe, PoseStack mx, AABB dims, final double scale = Minecraft.getInstance().getWindow().getGuiScale(); final Matrix4f matrix = mx.last().pose(); final FloatBuffer buf = BufferUtils.createFloatBuffer(16); - matrix.store(buf); + matrix.get(buf); // { x, y, z } Vec3 translation = new Vec3( @@ -360,14 +354,14 @@ private void renderRecipe(MiniaturizationRecipe recipe, PoseStack mx, AABB dims, buf.get(13) * scale, buf.get(14) * scale); - scissorBounds.x *= scale; - scissorBounds.y *= scale; - scissorBounds.width *= scale; - scissorBounds.height *= scale; - final int scissorX = Math.round(Math.round(translation.x + scissorBounds.x)); - final int scissorY = Math.round(Math.round(Minecraft.getInstance().getWindow().getHeight() - scissorBounds.y - scissorBounds.height - translation.y)); - final int scissorW = Math.round(scissorBounds.width); - final int scissorH = Math.round(scissorBounds.height); + int scaledX = (int)(scissorBounds.x * scale); + int scaledY = (int)(scissorBounds.y * scale); + int scaledWidth = (int)(scissorBounds.width * scale); + int scaledHeight = (int)(scissorBounds.height * scale); + final int scissorX = Math.round((float)(translation.x + scaledX)); + final int scissorY = Math.round((float)(Minecraft.getInstance().getWindow().getHeight() - scaledY - scaledHeight - translation.y)); + final int scissorW = Math.round((float)scaledWidth); + final int scissorH = Math.round((float)scaledHeight); RenderSystem.enableScissor(scissorX, scissorY, scissorW, scissorH); mx.pushPose(); @@ -397,10 +391,11 @@ private void renderRecipe(MiniaturizationRecipe recipe, PoseStack mx, AABB dims, private void drawActualRecipe(MiniaturizationRecipe recipe, PoseStack mx, AABB dims, MultiBufferSource.BufferSource buffers) { double gameTime = Minecraft.getInstance().level.getGameTime(); double test = Math.toDegrees(gameTime) / 15; - mx.mulPose(new Quaternion(35f, - (float) -test, - 0, - true)); + mx.mulPose(new Quaternionf().rotationXYZ( + (float) Math.toRadians(35f), + (float) Math.toRadians(-test), + 0 + )); double ySize = recipe.getDimensions().getYsize(); @@ -425,36 +420,34 @@ private void drawActualRecipe(MiniaturizationRecipe recipe, PoseStack mx, AABB d } } - private void renderPreviewControls(PoseStack mx, AABB dims) { + private void renderPreviewControls(GuiGraphics guiGraphics, AABB dims) { + PoseStack mx = guiGraphics.pose(); mx.pushPose(); mx.translate(0, 0, 10); - ResourceLocation sprites = new ResourceLocation(CompactCrafting.MOD_ID, "textures/gui/jei-sprites.png"); + ResourceLocation sprites = CompactCrafting.modRL("textures/gui/jei-sprites.png"); if (exploded) { - drawScaledTexture(mx, sprites, explodeToggle, 20, 0, 20, 20, 120, 20); + drawScaledTexture(guiGraphics, sprites, explodeToggle, 20, 0, 20, 20, 120, 20); } else { - drawScaledTexture(mx, sprites, explodeToggle, 0, 0, 20, 20, 120, 20); + drawScaledTexture(guiGraphics, sprites, explodeToggle, 0, 0, 20, 20, 120, 20); } - // Layer change buttons if (singleLayer) { - drawScaledTexture(mx, sprites, layerSwap, 60, 0, 20, 20, 120, 20); + drawScaledTexture(guiGraphics, sprites, layerSwap, 60, 0, 20, 20, 120, 20); } else { - drawScaledTexture(mx, sprites, layerSwap, 40, 0, 20, 20, 120, 20); + drawScaledTexture(guiGraphics, sprites, layerSwap, 40, 0, 20, 20, 120, 20); } if (singleLayer) { if (singleLayerOffset < dims.getYsize() - 1) - drawScaledTexture(mx, sprites, layerUp, 80, 0, 20, 20, 120, 20); + drawScaledTexture(guiGraphics, sprites, layerUp, 80, 0, 20, 20, 120, 20); if (singleLayerOffset > 0) { - drawScaledTexture(mx, sprites, layerDown, 100, 0, 20, 20, 120, 20); + drawScaledTexture(guiGraphics, sprites, layerDown, 100, 0, 20, 20, 120, 20); } } - // drawScaledTexture(mx, sprites, bindPlate, 120, 0, 20, 20, 140, 20); - mx.popPose(); } @@ -506,7 +499,7 @@ private void renderComponent(PoseStack mx, MultiBufferSource.BufferSource buffer blocks.renderSingleBlock(state1, mx, buffers, - LightTexture.FULL_SKY, + LightTexture.FULL_BRIGHT, OverlayTexture.NO_OVERLAY, data, null); } catch (Exception e) { diff --git a/neoforge-main/gtfo/compat/jei/JeiMiniaturizationPlugin.java b/neoforge-main/src/main/java/dev/compactmods/crafting/compat/jei/JeiMiniaturizationPlugin.java similarity index 86% rename from neoforge-main/gtfo/compat/jei/JeiMiniaturizationPlugin.java rename to neoforge-main/src/main/java/dev/compactmods/crafting/compat/jei/JeiMiniaturizationPlugin.java index 79955571..d0aead81 100644 --- a/neoforge-main/gtfo/compat/jei/JeiMiniaturizationPlugin.java +++ b/neoforge-main/src/main/java/dev/compactmods/crafting/compat/jei/JeiMiniaturizationPlugin.java @@ -19,7 +19,7 @@ public class JeiMiniaturizationPlugin implements IModPlugin { @Override public ResourceLocation getPluginUid() { - return new ResourceLocation(CompactCrafting.MOD_ID, "miniaturization_crafting"); + return CompactCrafting.modRL("miniaturization_crafting"); } @Override @@ -44,7 +44,8 @@ public void registerRecipes(IRecipeRegistration registration) { ClientLevel w = Minecraft.getInstance().level; RecipeManager rm = w == null ? null : w.getRecipeManager(); if(rm != null) { - final var miniRecipes = rm.getAllRecipesFor(CCMiniaturizationRecipes.MINIATURIZATION_RECIPE.get()); + final var miniRecipeHolders = rm.getAllRecipesFor(CCMiniaturizationRecipes.MINIATURIZATION_RECIPE.get()); + final var miniRecipes = miniRecipeHolders.stream().map(holder -> holder.value()).toList(); registration.addRecipes(JeiMiniaturizationCraftingCategory.RECIPE_TYPE, miniRecipes); } } diff --git a/neoforge-main/gtfo/compat/theoneprobe/TOPMain.java b/neoforge-main/src/main/java/dev/compactmods/crafting/compat/theoneprobe/TOPMain.java similarity index 100% rename from neoforge-main/gtfo/compat/theoneprobe/TOPMain.java rename to neoforge-main/src/main/java/dev/compactmods/crafting/compat/theoneprobe/TOPMain.java diff --git a/neoforge-main/gtfo/compat/theoneprobe/TheOneProbeCompat.java b/neoforge-main/src/main/java/dev/compactmods/crafting/compat/theoneprobe/TheOneProbeCompat.java similarity index 82% rename from neoforge-main/gtfo/compat/theoneprobe/TheOneProbeCompat.java rename to neoforge-main/src/main/java/dev/compactmods/crafting/compat/theoneprobe/TheOneProbeCompat.java index 48cef4a3..63b83105 100644 --- a/neoforge-main/gtfo/compat/theoneprobe/TheOneProbeCompat.java +++ b/neoforge-main/src/main/java/dev/compactmods/crafting/compat/theoneprobe/TheOneProbeCompat.java @@ -1,9 +1,9 @@ package dev.compactmods.crafting.compat.theoneprobe; -import net.minecraftforge.fml.InterModComms; -public class TheOneProbeCompat { +import net.neoforged.fml.InterModComms; +public class TheOneProbeCompat { public static void sendIMC() { InterModComms.sendTo("theoneprobe", "getTheOneProbe", TOPMain::new); diff --git a/neoforge-main/src/main/java/dev/compactmods/crafting/compat/theoneprobe/providers/FieldProjectorProvider.java b/neoforge-main/src/main/java/dev/compactmods/crafting/compat/theoneprobe/providers/FieldProjectorProvider.java new file mode 100644 index 00000000..f68ac378 --- /dev/null +++ b/neoforge-main/src/main/java/dev/compactmods/crafting/compat/theoneprobe/providers/FieldProjectorProvider.java @@ -0,0 +1,105 @@ +package dev.compactmods.crafting.compat.theoneprobe.providers; + +import dev.compactmods.crafting.CompactCrafting; +import dev.compactmods.crafting.api.EnumCraftingState; +import dev.compactmods.crafting.api.field.IMiniaturizationField; +import dev.compactmods.crafting.data.CCAttachments; +import dev.compactmods.crafting.projector.FieldProjectorBlock; +import dev.compactmods.crafting.projector.FieldProjectorEntity; +import dev.compactmods.crafting.projector.ProjectorHelper; +import dev.compactmods.crafting.recipes.MiniaturizationRecipe; +import mcjty.theoneprobe.api.Color; +import mcjty.theoneprobe.api.ElementAlignment; +import mcjty.theoneprobe.api.IProbeHitData; +import mcjty.theoneprobe.api.IProbeInfo; +import mcjty.theoneprobe.api.IProbeInfoProvider; +import mcjty.theoneprobe.api.ProbeMode; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.state.BlockState; + +import java.util.Set; + +public class FieldProjectorProvider implements IProbeInfoProvider { + + private static final ResourceLocation ID = CompactCrafting.modRL("field_projector"); + @Override + public ResourceLocation getID() { + return ID; + } + + @Override + public void addProbeInfo(ProbeMode mode, IProbeInfo info, Player player, Level level, BlockState state, IProbeHitData hitData) { + if (!(state.getBlock() instanceof FieldProjectorBlock)) + return; + + // add info from server + if (FieldProjectorBlock.isActive(state) && level.getBlockEntity(hitData.getPos()) instanceof FieldProjectorEntity mFieldEntity) { + // Find the field this projector belongs to + ProjectorHelper.getClosestOppositeSize(level, hitData.getPos()).ifPresent(size -> { + final var center = size.getCenterFromProjector(hitData.getPos(), state.getValue(FieldProjectorBlock.FACING)); + level.getData(CCAttachments.ACTIVE_FIELDS).get(center).ifPresent(field -> { + MiniaturizationRecipe recipe = field.recipeHolder() != null ? field.recipeHolder().value() : null; + + final IProbeInfo recipeProgress = info.vertical( + info.defaultLayoutStyle() + .alignment(ElementAlignment.ALIGN_CENTER) + .spacing(1)); + + IProbeInfo group = recipeProgress.horizontal(info.defaultLayoutStyle() + .alignment(ElementAlignment.ALIGN_CENTER)); + + // group.text(new TranslationTextComponent(CompactCrafting.MOD_ID + ".top.current_recipe")); + + if(recipe != null) { + int progress = field.getProgress(); + + group.item(recipe.catalyst()); + + group.icon( + CompactCrafting.modRL( "textures/gui/jei-arrow-outputs.png"), + 0, 0, 24, 19, info.defaultIconStyle().textureHeight(19) + .textureWidth(24) + .height(19).width(24)); + + for (ItemStack out : recipe.getOutputs()) { + group.item(out); + } + + recipeProgress + .progress(1, 1, info.defaultProgressStyle() + .showText(false).borderColor(0).backgroundColor(0) + .height(0).width(110).filledColor(0) + .alternateFilledColor(0)) + + .progress(progress, recipe.getCraftingTime(), + info.defaultProgressStyle() + .height(5) + .width(100) + .showText(false) + .filledColor(0xFFCCCCCC) + .alternateFilledColor(0xFFCCCCCC) + .backgroundColor(Color.rgb(255, 250, 250, 50)) + .borderColor(0x00000000)) + ; + + if (field.getCraftingState() == EnumCraftingState.MATCHED && progress == 0) { + recipeProgress + .text(Component.translatable(CompactCrafting.MOD_ID + ".top.awaiting_catalyst")); + + } else { + if(mode == ProbeMode.EXTENDED) { + recipeProgress + .text(Component.translatable(CompactCrafting.MOD_ID + ".top.progress", progress, recipe.getCraftingTime())); + } + } + } + }); + }); + } + } +} diff --git a/neoforge-main/gtfo/compat/theoneprobe/providers/FieldProxyProvider.java b/neoforge-main/src/main/java/dev/compactmods/crafting/compat/theoneprobe/providers/FieldProxyProvider.java similarity index 61% rename from neoforge-main/gtfo/compat/theoneprobe/providers/FieldProxyProvider.java rename to neoforge-main/src/main/java/dev/compactmods/crafting/compat/theoneprobe/providers/FieldProxyProvider.java index cb4270e2..3b4a7ae5 100644 --- a/neoforge-main/gtfo/compat/theoneprobe/providers/FieldProxyProvider.java +++ b/neoforge-main/src/main/java/dev/compactmods/crafting/compat/theoneprobe/providers/FieldProxyProvider.java @@ -1,7 +1,7 @@ package dev.compactmods.crafting.compat.theoneprobe.providers; import dev.compactmods.crafting.CompactCrafting; -import dev.compactmods.crafting.core.CCCapabilities; +import dev.compactmods.crafting.data.CCAttachments; import dev.compactmods.crafting.proxies.block.FieldProxyBlock; import dev.compactmods.crafting.proxies.data.BaseFieldProxyEntity; import mcjty.theoneprobe.api.IProbeHitData; @@ -16,7 +16,7 @@ import net.minecraft.world.level.block.state.BlockState; public class FieldProxyProvider implements IProbeInfoProvider { - private static final ResourceLocation ID = new ResourceLocation(CompactCrafting.MOD_ID, "field_proxy"); + private static final ResourceLocation ID = CompactCrafting.modRL("field_proxy"); @Override public ResourceLocation getID() { @@ -29,14 +29,19 @@ public void addProbeInfo(ProbeMode mode, IProbeInfo info, Player player, Level l return; BaseFieldProxyEntity tile = (BaseFieldProxyEntity) level.getBlockEntity(hitData.getPos()); - if(tile == null) + if(tile == null || tile.fieldCenter == null) return; - tile.getCapability(CCCapabilities.MINIATURIZATION_FIELD) - .ifPresent(field -> { - BlockPos fieldCenter = field.getCenter(); + var fields = level.getData(CCAttachments.ACTIVE_FIELDS); + fields.get(tile.fieldCenter).ifPresent(field -> { + BlockPos fieldCenter = field.getCenter(); + info.text(Component.translatable("compactcrafting.top.proxy_bound", fieldCenter.toString())); - info.text(Component.translatable("tooltip.compactcrafting.proxy_bound", fieldCenter.toString())); - }); + if (field.currentRecipe() != null) { + info.text(Component.translatable("compactcrafting.top.proxy_has_recipe")); + } else { + info.text(Component.translatable("compactcrafting.top.proxy_no_recipe")); + } + }); } } diff --git a/neoforge-main/src/main/java/dev/compactmods/crafting/core/CCBlocks.java b/neoforge-main/src/main/java/dev/compactmods/crafting/core/CCBlocks.java index d090ff46..d21cd837 100644 --- a/neoforge-main/src/main/java/dev/compactmods/crafting/core/CCBlocks.java +++ b/neoforge-main/src/main/java/dev/compactmods/crafting/core/CCBlocks.java @@ -3,6 +3,10 @@ import dev.compactmods.crafting.CompactCrafting; import dev.compactmods.crafting.projector.FieldProjectorBlock; import dev.compactmods.crafting.projector.FieldProjectorEntity; +import dev.compactmods.crafting.proxies.block.MatchFieldProxyBlock; +import dev.compactmods.crafting.proxies.block.RescanFieldProxyBlock; +import dev.compactmods.crafting.proxies.data.MatchFieldProxyEntity; +import dev.compactmods.crafting.proxies.data.RescanFieldProxyEntity; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.state.BlockBehaviour; @@ -23,15 +27,31 @@ public class CCBlocks { .requiresCorrectToolForDrops() )); -// static final Supplier PROXY_PROPS = () -> BlockBehaviour.Properties.of() -// .strength(8, 20) -// .requiresCorrectToolForDrops(); + public static final DeferredBlock RESCAN_FIELD_PROXY_BLOCK = BLOCKS.register("rescan_proxy", () -> + new RescanFieldProxyBlock(BlockBehaviour.Properties.of() + .strength(8, 20) + .requiresCorrectToolForDrops())); + + public static final DeferredBlock MATCH_FIELD_PROXY_BLOCK = BLOCKS.register("match_proxy", () -> + new MatchFieldProxyBlock(BlockBehaviour.Properties.of() + .strength(8, 20) + .requiresCorrectToolForDrops())); public static final DeferredHolder, BlockEntityType> FIELD_PROJECTOR_TILE = BLOCK_ENTITIES.register("field_projector", () -> BlockEntityType.Builder .of(FieldProjectorEntity::new, FIELD_PROJECTOR_BLOCK.get()) .build(null)); + public static final DeferredHolder, BlockEntityType> RESCAN_PROXY_ENTITY = BLOCK_ENTITIES.register("rescan_proxy", () -> + BlockEntityType.Builder + .of(RescanFieldProxyEntity::new, RESCAN_FIELD_PROXY_BLOCK.get()) + .build(null)); + + public static final DeferredHolder, BlockEntityType> MATCH_PROXY_ENTITY = BLOCK_ENTITIES.register("match_proxy", () -> + BlockEntityType.Builder + .of(MatchFieldProxyEntity::new, MATCH_FIELD_PROXY_BLOCK.get()) + .build(null)); + public static void init(IEventBus bus) { BLOCKS.register(bus); BLOCK_ENTITIES.register(bus); diff --git a/neoforge-main/src/main/java/dev/compactmods/crafting/core/CCDataComponents.java b/neoforge-main/src/main/java/dev/compactmods/crafting/core/CCDataComponents.java new file mode 100644 index 00000000..aa17fbff --- /dev/null +++ b/neoforge-main/src/main/java/dev/compactmods/crafting/core/CCDataComponents.java @@ -0,0 +1,47 @@ +package dev.compactmods.crafting.core; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import dev.compactmods.crafting.CompactCrafting; +import net.minecraft.core.BlockPos; +import net.minecraft.core.component.DataComponentType; +import net.minecraft.core.registries.Registries; +import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.network.codec.ByteBufCodecs; +import net.minecraft.network.codec.StreamCodec; +import net.neoforged.bus.api.IEventBus; +import net.neoforged.neoforge.registries.DeferredHolder; +import net.neoforged.neoforge.registries.DeferredRegister; + +public class CCDataComponents { + + private static final DeferredRegister.DataComponents DATA_COMPONENTS = + DeferredRegister.createDataComponents(CompactCrafting.MOD_ID); + + public record FieldCenter(BlockPos center) { + } + + public static final Codec FIELD_CENTER_CODEC = RecordCodecBuilder.create(instance -> + instance.group( + BlockPos.CODEC.fieldOf("center").forGetter(FieldCenter::center) + ).apply(instance, FieldCenter::new) + ); + + public static final StreamCodec FIELD_CENTER_STREAM_CODEC = + StreamCodec.composite( + BlockPos.STREAM_CODEC, FieldCenter::center, + FieldCenter::new + ); + + public static final DeferredHolder, DataComponentType> FIELD_CENTER = + DATA_COMPONENTS.registerComponentType( + "field_center", + builder -> builder + .persistent(FIELD_CENTER_CODEC) + .networkSynchronized(FIELD_CENTER_STREAM_CODEC) + ); + + public static void init(IEventBus bus) { + DATA_COMPONENTS.register(bus); + } +} \ No newline at end of file diff --git a/neoforge-main/src/main/java/dev/compactmods/crafting/core/CCItems.java b/neoforge-main/src/main/java/dev/compactmods/crafting/core/CCItems.java index 901c3322..ee66fe18 100644 --- a/neoforge-main/src/main/java/dev/compactmods/crafting/core/CCItems.java +++ b/neoforge-main/src/main/java/dev/compactmods/crafting/core/CCItems.java @@ -2,6 +2,7 @@ import dev.compactmods.crafting.CompactCrafting; import dev.compactmods.crafting.items.FieldProjectorItem; +import dev.compactmods.crafting.proxies.item.FieldProxyItem; import net.minecraft.world.item.Item; import net.neoforged.bus.api.IEventBus; import net.neoforged.neoforge.registries.DeferredItem; @@ -26,11 +27,11 @@ public class CCItems { public static final DeferredItem BASE_ITEM = ITEMS.register("base", () -> new Item(BASE_ITEM_PROPS.get())); -// public static final DeferredItem RESCAN_PROXY_ITEM = ITEMS.register("rescan_proxy", () -> -// new FieldProxyItem(CCBlocks.RESCAN_FIELD_PROXY_BLOCK.get(), BASE_ITEM_PROPS.get())); -// -// public static final DeferredItem MATCH_PROXY_ITEM = ITEMS.register("match_proxy", () -> -// new FieldProxyItem(CCBlocks.MATCH_FIELD_PROXY_BLOCK.get(), BASE_ITEM_PROPS.get())); + public static final DeferredItem RESCAN_PROXY_ITEM = ITEMS.register("rescan_proxy", () -> + new FieldProxyItem(CCBlocks.RESCAN_FIELD_PROXY_BLOCK.get(), BASE_ITEM_PROPS.get())); + + public static final DeferredItem MATCH_PROXY_ITEM = ITEMS.register("match_proxy", () -> + new FieldProxyItem(CCBlocks.MATCH_FIELD_PROXY_BLOCK.get(), BASE_ITEM_PROPS.get())); // ================================================================================================================ diff --git a/neoforge-main/src/main/java/dev/compactmods/crafting/core/CreativeTabs.java b/neoforge-main/src/main/java/dev/compactmods/crafting/core/CreativeTabs.java index 8853bc36..214ac076 100644 --- a/neoforge-main/src/main/java/dev/compactmods/crafting/core/CreativeTabs.java +++ b/neoforge-main/src/main/java/dev/compactmods/crafting/core/CreativeTabs.java @@ -18,6 +18,10 @@ public interface CreativeTabs { .title(Component.translatable("itemGroup.compactcrafting")) .displayItems((params, out) -> { out.accept(CCItems.FIELD_PROJECTOR_ITEM.get()); + out.accept(CCItems.RESCAN_PROXY_ITEM.get()); + out.accept(CCItems.MATCH_PROXY_ITEM.get()); + out.accept(CCItems.PROJECTOR_DISH_ITEM.get()); + out.accept(CCItems.BASE_ITEM.get()); }) .build()); diff --git a/neoforge-main/src/main/java/dev/compactmods/crafting/data/CCAttachments.java b/neoforge-main/src/main/java/dev/compactmods/crafting/data/CCAttachments.java index 1f2eb445..bcefcce3 100644 --- a/neoforge-main/src/main/java/dev/compactmods/crafting/data/CCAttachments.java +++ b/neoforge-main/src/main/java/dev/compactmods/crafting/data/CCAttachments.java @@ -1,10 +1,9 @@ package dev.compactmods.crafting.data; -import com.mojang.serialization.Codec; import dev.compactmods.crafting.CompactCrafting; import dev.compactmods.crafting.field.ActiveWorldFields; import net.minecraft.core.HolderLookup; -import net.minecraft.nbt.NbtOps; +import net.minecraft.nbt.ListTag; import net.minecraft.nbt.Tag; import net.minecraft.world.level.Level; import net.neoforged.neoforge.attachment.AttachmentType; @@ -15,42 +14,29 @@ import net.neoforged.neoforge.registries.NeoForgeRegistries; import org.jetbrains.annotations.NotNull; -import java.util.function.BiConsumer; - public interface CCAttachments { DeferredRegister> ATTACHMENT_TYPES = DeferredRegister.create(NeoForgeRegistries.ATTACHMENT_TYPES, CompactCrafting.MOD_ID); DeferredHolder, AttachmentType> ACTIVE_FIELDS = ATTACHMENT_TYPES - .register("active_fields", () -> AttachmentType.builder((holder) -> ActiveWorldFields.create((Level) holder)).build()); - - static IAttachmentSerializer holderWith(Codec codec, BiConsumer setter) { - return new IAttachmentSerializer<>() { - @Override - public @NotNull T read(@NotNull IAttachmentHolder holder, @NotNull Tag tag, HolderLookup.@NotNull Provider provider) { - var parse = codec.parse(provider.createSerializationContext(NbtOps.INSTANCE), tag); - if (parse.error().isPresent()) { - throw new RuntimeException(parse.error().get().toString()); - } - if (parse.result().isEmpty()) - throw new RuntimeException("Result not present"); - - var data = parse.result().get(); - setter.accept(data, holder); - return data; + .register("active_fields", () -> AttachmentType.builder((holder) -> ActiveWorldFields.create((Level) holder)) + .serialize(new ActiveWorldFieldsSerializer()) + .build()); + + class ActiveWorldFieldsSerializer implements IAttachmentSerializer { + @Override + public @NotNull ActiveWorldFields read(@NotNull IAttachmentHolder holder, @NotNull Tag tag, HolderLookup.@NotNull Provider provider) { + Level level = (Level) holder; + ActiveWorldFields fields = ActiveWorldFields.create(level); + if (tag instanceof ListTag listTag) { + fields.deserializeNBT(listTag); } + return fields; + } - @Override - public Tag write(@NotNull T attachment, HolderLookup.@NotNull Provider provider) { - var encode = codec.encodeStart(provider.createSerializationContext(NbtOps.INSTANCE), attachment); - if (encode.error().isPresent()) { - throw new RuntimeException(encode.error().get().toString()); - } - if (encode.result().isEmpty()) - throw new RuntimeException("Result not present"); - - return encode.result().get(); - } - }; + @Override + public Tag write(@NotNull ActiveWorldFields attachment, HolderLookup.@NotNull Provider provider) { + return attachment.serializeNBT(); + } } } diff --git a/neoforge-main/src/main/java/dev/compactmods/crafting/events/WorldEventHandler.java b/neoforge-main/src/main/java/dev/compactmods/crafting/events/WorldEventHandler.java index f8e804b3..5e93d3e7 100644 --- a/neoforge-main/src/main/java/dev/compactmods/crafting/events/WorldEventHandler.java +++ b/neoforge-main/src/main/java/dev/compactmods/crafting/events/WorldEventHandler.java @@ -3,6 +3,10 @@ import dev.compactmods.crafting.CompactCrafting; import dev.compactmods.crafting.data.CCAttachments; import dev.compactmods.crafting.field.ActiveWorldFields; +import dev.compactmods.crafting.field.MiniaturizationField; +import dev.compactmods.crafting.network.ClientFieldWatchPacket; +import dev.compactmods.crafting.network.ClientFieldUnwatchPacket; +import dev.compactmods.crafting.network.FieldActivatedPacket; import io.reactivex.rxjava3.subjects.PublishSubject; import io.reactivex.rxjava3.subjects.Subject; import net.minecraft.server.level.ServerLevel; @@ -10,10 +14,12 @@ import net.minecraft.world.level.ChunkPos; import net.neoforged.bus.api.SubscribeEvent; import net.neoforged.fml.common.EventBusSubscriber; +import net.neoforged.neoforge.event.entity.player.PlayerEvent; import net.neoforged.neoforge.event.level.ChunkEvent; import net.neoforged.neoforge.event.level.ChunkWatchEvent; import net.neoforged.neoforge.event.server.ServerStartedEvent; import net.neoforged.neoforge.event.tick.LevelTickEvent; +import net.neoforged.neoforge.network.PacketDistributor; @SuppressWarnings("unused") @EventBusSubscriber(modid = CompactCrafting.MOD_ID) @@ -28,18 +34,19 @@ public class WorldEventHandler { @SubscribeEvent public static void onServerStarted(final ServerStartedEvent evt) { CompactCrafting.LOGGER.trace("Server started; calling previously active fields to validate themselves."); -// for (ServerLevel level : evt.getServer().getAllLevels()) { -// // FIXME -//// level.getCapability(CCCapabilities.FIELDS) -//// .resolve() -//// .ifPresent(fields -> { -//// fields.setLevel(level); -//// fields.getFields().forEach(f -> { -//// f.setLevel(level); -//// f.checkLoaded(); -//// }); -//// }); -// } + for (ServerLevel level : evt.getServer().getAllLevels()) { + level.getExistingData(CCAttachments.ACTIVE_FIELDS).ifPresent(fields -> { + fields.getFields().forEach(f -> { + if (f instanceof MiniaturizationField mf) { + mf.checkLoaded(); + // Send field activation to all players tracking the chunk + ChunkPos chunkPos = new ChunkPos(mf.getCenter()); + FieldActivatedPacket packet = new FieldActivatedPacket(mf, mf.serverData()); + PacketDistributor.sendToPlayersTrackingChunk(level, chunkPos, packet); + } + }); + }); + } } @SubscribeEvent @@ -53,19 +60,12 @@ public static void onStartChunkTracking(final ChunkWatchEvent.Watch event) { final ChunkPos pos = event.getPos(); final ServerLevel level = event.getLevel(); - // FIXME -// level.getCapability(CCCapabilities.FIELDS) -// .map(f -> f.getFields(pos)) -// .ifPresent(activeFields -> { -// activeFields.forEach(field -> { -// ClientFieldWatchPacket pkt = new ClientFieldWatchPacket(field); -// -// NetworkHandler.MAIN_CHANNEL.send( -// PacketDistributor.PLAYER.with(() -> player), -// pkt -// ); -// }); -// }); + level.getExistingData(CCAttachments.ACTIVE_FIELDS).ifPresent(fields -> { + fields.getFields(pos).forEach(field -> { + ClientFieldWatchPacket pkt = new ClientFieldWatchPacket(field); + PacketDistributor.sendToPlayer(player, pkt); + }); + }); } @SubscribeEvent @@ -74,19 +74,12 @@ public static void onStopChunkTracking(final ChunkWatchEvent.UnWatch event) { final ChunkPos pos = event.getPos(); final ServerLevel level = event.getLevel(); - // FIXME -// level.getCapability(CCCapabilities.FIELDS) -// .map(f -> f.getFields(pos)) -// .ifPresent(activeFields -> { -// activeFields.forEach(field -> { -// ClientFieldUnwatchPacket pkt = new ClientFieldUnwatchPacket(field.getCenter()); -// -// NetworkHandler.MAIN_CHANNEL.send( -// PacketDistributor.PLAYER.with(() -> player), -// pkt -// ); -// }); -// }); + level.getExistingData(CCAttachments.ACTIVE_FIELDS).ifPresent(fields -> { + fields.getFields(pos).forEach(field -> { + ClientFieldUnwatchPacket pkt = new ClientFieldUnwatchPacket(field.getCenter()); + PacketDistributor.sendToPlayer(player, pkt); + }); + }); } @SubscribeEvent @@ -98,4 +91,24 @@ public static void chunkLoaded(final ChunkEvent.Load cEvent) { public static void chunkUnloaded(final ChunkEvent.Unload cEvent) { CHUNK_CHANGES.onNext(cEvent); } + + @SubscribeEvent + public static void onPlayerLogin(final PlayerEvent.PlayerLoggedInEvent event) { + if (event.getEntity() instanceof ServerPlayer player) { + ServerLevel level = player.serverLevel(); + // Send all active fields in the player's view distance + level.getExistingData(CCAttachments.ACTIVE_FIELDS).ifPresent(fields -> { + fields.getFields().forEach(field -> { + if (field instanceof MiniaturizationField mf) { + // Check if player can see this field's chunk + ChunkPos fieldChunk = new ChunkPos(mf.getCenter()); + if (player.getChunkTrackingView().contains(fieldChunk.x, fieldChunk.z)) { + FieldActivatedPacket packet = new FieldActivatedPacket(mf, mf.serverData()); + PacketDistributor.sendToPlayer(player, packet); + } + } + }); + }); + } + } } diff --git a/neoforge-main/src/main/java/dev/compactmods/crafting/fakeworld/RecipeChunk.java b/neoforge-main/src/main/java/dev/compactmods/crafting/fakeworld/RecipeChunk.java new file mode 100644 index 00000000..793e16d2 --- /dev/null +++ b/neoforge-main/src/main/java/dev/compactmods/crafting/fakeworld/RecipeChunk.java @@ -0,0 +1,85 @@ +package dev.compactmods.crafting.fakeworld; + +import javax.annotation.Nullable; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import dev.compactmods.crafting.api.components.IRecipeBlockComponent; +import dev.compactmods.crafting.api.recipe.layers.IRecipeLayer; +import dev.compactmods.crafting.recipes.MiniaturizationRecipe; +import dev.compactmods.crafting.util.BlockSpaceUtil; +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.ChunkPos; +import net.minecraft.world.level.biome.Biomes; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.EntityBlock; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.chunk.EmptyLevelChunk; +import net.minecraft.world.level.material.FluidState; +import net.minecraft.core.registries.Registries; + +public class RecipeChunk extends EmptyLevelChunk { + private final MiniaturizationRecipe recipe; + private final Map blockCache; + private final Map tileCache; + + public RecipeChunk(RenderingWorld renderingLevel, ChunkPos chunkPos, MiniaturizationRecipe recipe) { + super(renderingLevel, chunkPos, renderingLevel.registryAccess().registryOrThrow(Registries.BIOME).getHolderOrThrow(Biomes.THE_VOID)); + this.recipe = recipe; + + this.blockCache = new HashMap<>(); + this.tileCache = new HashMap<>(); + + BlockSpaceUtil.getBlocksIn(recipe.getDimensions()).forEach(pos -> { + int y = pos.getY(); + Optional layer = recipe.getLayer(y); + + if(!layer.isPresent()) + return; + + IRecipeLayer rLayer = layer.get(); + Optional componentForPosition = rLayer.getComponentForPosition(pos.below(y)); + + BlockState posState = componentForPosition + .flatMap(recipe.getComponents()::getBlock) + .map(IRecipeBlockComponent::getRenderState) + .orElse(Blocks.VOID_AIR.defaultBlockState()); + + blockCache.put(pos, posState); + + if(posState.getBlock() instanceof EntityBlock eb) { + BlockEntity tile = eb.newBlockEntity(pos.immutable(), posState); + if(tile != null) { + tile.setLevel(renderingLevel); + tileCache.put(pos.immutable(), tile); + } + } + }); + } + + @Override + public BlockState getBlockState(BlockPos pos) { + if(blockCache.containsKey(pos)) + return blockCache.get(pos); + + return Blocks.VOID_AIR.defaultBlockState(); + } + + @Override + public FluidState getFluidState(BlockPos pos) { + return getBlockState(pos).getFluidState(); + } + + @Nullable + @Override + public BlockEntity getBlockEntity(BlockPos pos) { + return tileCache.get(pos); + } + + @Nullable + @Override + public BlockEntity getBlockEntity(BlockPos pos, EntityCreationType createType) { + return tileCache.get(pos); + } +} \ No newline at end of file diff --git a/neoforge-main/src/main/java/dev/compactmods/crafting/fakeworld/RenderingChunkProvider.java b/neoforge-main/src/main/java/dev/compactmods/crafting/fakeworld/RenderingChunkProvider.java new file mode 100644 index 00000000..6661e1cb --- /dev/null +++ b/neoforge-main/src/main/java/dev/compactmods/crafting/fakeworld/RenderingChunkProvider.java @@ -0,0 +1,83 @@ +package dev.compactmods.crafting.fakeworld; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.BooleanSupplier; +import java.util.stream.Collectors; +import com.mojang.datafixers.util.Pair; +import dev.compactmods.crafting.recipes.MiniaturizationRecipe; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Holder; +import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.ChunkPos; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.biome.Biomes; +import net.minecraft.world.level.chunk.ChunkAccess; +import net.minecraft.world.level.chunk.ChunkSource; +import net.minecraft.world.level.chunk.EmptyLevelChunk; +import net.minecraft.world.level.chunk.status.ChunkStatus; +import net.minecraft.world.level.lighting.LevelLightEngine; +import net.minecraft.core.registries.Registries; +import org.jetbrains.annotations.Nullable; + +public class RenderingChunkProvider extends ChunkSource { + private final Holder VOID; + private final MiniaturizationRecipe recipe; + + private final Map chunks; + private final RenderingWorld renderingLevel; + private final LevelLightEngine lightManager; + + public RenderingChunkProvider(RenderingWorld renderingLevel, MiniaturizationRecipe recipe) { + VOID = renderingLevel.registryAccess().registryOrThrow(Registries.BIOME).getHolderOrThrow(Biomes.THE_VOID); + + this.recipe = recipe; + + this.renderingLevel = renderingLevel; + this.lightManager = new LevelLightEngine(this, true, true); + + Map> byChunk = new HashMap<>(); + BlockPos.betweenClosedStream(this.recipe.getDimensions()) + .map(BlockPos::immutable) + .forEach(pos -> { + byChunk.computeIfAbsent(new ChunkPos(pos), $ -> new ArrayList<>()).add(pos); + }); + + chunks = byChunk.keySet().stream() + .map(chunkPos -> Pair.of(chunkPos, new RecipeChunk(this.renderingLevel, chunkPos, recipe))) + .collect(Collectors.toMap(Pair::getFirst, Pair::getSecond)); + } + + @Nullable + @Override + public ChunkAccess getChunk(int cx, int cz, ChunkStatus status, boolean load) { + return chunks.computeIfAbsent(new ChunkPos(cx, cz), p -> new EmptyLevelChunk(renderingLevel, p, VOID)); + } + + @Override + public void tick(BooleanSupplier bool, boolean bool2) { + + } + + @Override + public String gatherStats() { + return "?"; + } + + @Override + public int getLoadedChunksCount() { + return chunks.size(); + } + + @Override + public LevelLightEngine getLightEngine() { + return lightManager; + } + + @Override + public BlockGetter getLevel() { + return renderingLevel; + } +} diff --git a/neoforge-main/src/main/java/dev/compactmods/crafting/fakeworld/RenderingSpawnInfo.java b/neoforge-main/src/main/java/dev/compactmods/crafting/fakeworld/RenderingSpawnInfo.java new file mode 100644 index 00000000..cb97f240 --- /dev/null +++ b/neoforge-main/src/main/java/dev/compactmods/crafting/fakeworld/RenderingSpawnInfo.java @@ -0,0 +1,82 @@ +package dev.compactmods.crafting.fakeworld; + +import net.minecraft.core.BlockPos; +import net.minecraft.world.Difficulty; +import net.minecraft.world.level.GameRules; +import net.minecraft.world.level.storage.WritableLevelData; + +public class RenderingSpawnInfo implements WritableLevelData { + private static final GameRules RULES = new GameRules(); + + private BlockPos spawnPos = BlockPos.ZERO; + private float spawnAngle; + + @Override + public void setSpawn(BlockPos pos, float angle) { + this.spawnPos = pos; + this.spawnAngle = angle; + } + + @Override + public BlockPos getSpawnPos() { + return spawnPos; + } + + @Override + public float getSpawnAngle() { + return spawnAngle; + } + + @Override + public long getGameTime() + { + return 0; + } + + @Override + public long getDayTime() + { + return 0; + } + + @Override + public boolean isThundering() + { + return false; + } + + @Override + public boolean isRaining() + { + return false; + } + + @Override + public void setRaining(boolean isRaining) + { + + } + + @Override + public boolean isHardcore() + { + return false; + } + + @Override + public GameRules getGameRules() { + return RULES; + } + + @Override + public Difficulty getDifficulty() + { + return Difficulty.PEACEFUL; + } + + @Override + public boolean isDifficultyLocked() + { + return false; + } +} diff --git a/neoforge-main/src/main/java/dev/compactmods/crafting/fakeworld/RenderingWorld.java b/neoforge-main/src/main/java/dev/compactmods/crafting/fakeworld/RenderingWorld.java new file mode 100644 index 00000000..2383fa10 --- /dev/null +++ b/neoforge-main/src/main/java/dev/compactmods/crafting/fakeworld/RenderingWorld.java @@ -0,0 +1,224 @@ +package dev.compactmods.crafting.fakeworld; + +import dev.compactmods.crafting.recipes.MiniaturizationRecipe; +import net.minecraft.client.Minecraft; +import net.minecraft.core.*; +import net.minecraft.core.registries.Registries; +import net.minecraft.sounds.SoundEvent; +import net.minecraft.sounds.SoundSource; +import net.minecraft.util.profiling.InactiveProfiler; +import net.minecraft.world.TickRateManager; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.flag.FeatureFlagSet; +import net.minecraft.world.flag.FeatureFlags; +import net.minecraft.world.item.crafting.RecipeManager; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.LightLayer; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.biome.Biomes; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.chunk.ChunkSource; +import net.minecraft.world.level.dimension.BuiltinDimensionTypes; +import net.minecraft.world.level.entity.LevelEntityGetter; +import net.minecraft.world.level.gameevent.GameEvent; +import net.minecraft.world.level.material.Fluid; +import net.minecraft.world.level.saveddata.maps.MapId; +import net.minecraft.world.level.saveddata.maps.MapItemSavedData; +import net.minecraft.world.item.alchemy.PotionBrewing; +import net.minecraft.world.phys.Vec3; +import net.minecraft.world.scores.Scoreboard; +import net.minecraft.world.ticks.BlackholeTickAccess; +import net.minecraft.world.ticks.LevelTickAccess; + +import org.jetbrains.annotations.Nullable; +import java.util.Collections; +import java.util.List; + +public class RenderingWorld extends Level { + + private final MiniaturizationRecipe recipe; + + private final Scoreboard scoreboard = new Scoreboard(); + private final RecipeManager recipeManager; + private final RenderingChunkProvider chunkProvider; + + public RenderingWorld(MiniaturizationRecipe recipe) { + super(new RenderingSpawnInfo(), Level.OVERWORLD, + Minecraft.getInstance().level.registryAccess(), + Minecraft.getInstance().level.registryAccess().registryOrThrow(Registries.DIMENSION_TYPE).getHolderOrThrow(BuiltinDimensionTypes.OVERWORLD), + () -> InactiveProfiler.INSTANCE, true, false, 0, 1000000); + + this.recipe = recipe; + this.recipeManager = new RecipeManager(Minecraft.getInstance().level.registryAccess()); + this.chunkProvider = new RenderingChunkProvider(this, recipe); + } + + @Override + public void sendBlockUpdated(BlockPos p_184138_1_, BlockState p_184138_2_, BlockState p_184138_3_, int p_184138_4_) { + + } + + @Override + public void playSeededSound(@Nullable Player p_262953_, double p_263004_, double p_263398_, double p_263376_, Holder p_263359_, SoundSource p_263020_, float p_263055_, float p_262914_, long p_262991_) { + + } + + @Override + public void playSeededSound(@org.jetbrains.annotations.Nullable Player p_220363_, double p_220364_, double p_220365_, double p_220366_, SoundEvent p_220367_, SoundSource p_220368_, float p_220369_, float p_220370_, long p_220371_) { + + } + + @Override + public void playSeededSound(@Nullable Player p_220372_, Entity p_220373_, Holder p_263500_, SoundSource p_220375_, float p_220376_, float p_220377_, long p_220378_) { + + } + + @Override + public void playSound(@Nullable Player p_184148_1_, double p_184148_2_, double p_184148_4_, double p_184148_6_, SoundEvent p_184148_8_, SoundSource p_184148_9_, float p_184148_10_, float p_184148_11_) { + + } + + @Override + public void playSound(@Nullable Player p_217384_1_, Entity p_217384_2_, SoundEvent p_217384_3_, SoundSource p_217384_4_, float p_217384_5_, float p_217384_6_) { + + } + + @Override + public String gatherChunkSourceStats() { + return null; + } + + @Nullable + @Override + public Entity getEntity(int p_73045_1_) { + return null; + } + + @Override + public TickRateManager tickRateManager() { + return null; + } + + @Nullable + @Override + public MapItemSavedData getMapData(MapId mapId) { + return null; + } + + @Override + public void setMapData(MapId mapId, MapItemSavedData mapData) { + + } + + @Override + public MapId getFreeMapId() { + return new MapId(0); + } + + @Override + public void destroyBlockProgress(int p_175715_1_, BlockPos p_175715_2_, int p_175715_3_) { + + } + + @Override + public Scoreboard getScoreboard() { + return scoreboard; + } + + @Override + public RecipeManager getRecipeManager() { + return recipeManager; + } + + @Override + protected LevelEntityGetter getEntities() { + return null; + } + + @Override + public LevelTickAccess getBlockTicks() { + return BlackholeTickAccess.emptyLevelList(); + } + + @Override + public LevelTickAccess getFluidTicks() { + return BlackholeTickAccess.emptyLevelList(); + } + + @Override + public ChunkSource getChunkSource() { + return chunkProvider; + } + + @Override + public void levelEvent(@Nullable Player p_217378_1_, int p_217378_2_, BlockPos p_217378_3_, int p_217378_4_) { + + } + + @Override + public void gameEvent(Holder holder, Vec3 vec3, GameEvent.Context context) { + + } + + @Override + public RegistryAccess registryAccess() { + return Minecraft.getInstance().level.registryAccess(); + } + + @Override + public FeatureFlagSet enabledFeatures() { + return FeatureFlags.DEFAULT_FLAGS; + } + + @Override + public float getShade(Direction p_230487_1_, boolean p_230487_2_) { + return 1.0f; + } + + @Override + public int getBrightness(LightLayer lightType, BlockPos blockPos) { + return 15; + } + + @Override + public int getRawBrightness(BlockPos pos, int maxValue) { + return 15; + } + + @Override + public List players() { + return Collections.emptyList(); + } + + @Override + public Holder getUncachedNoiseBiome(int x, int y, int z) { + return registryAccess().registryOrThrow(net.minecraft.core.registries.Registries.BIOME).getHolderOrThrow(Biomes.THE_VOID); + } + + @Override + public PotionBrewing potionBrewing() { + return PotionBrewing.EMPTY; + } + + @Override + public void setDayTimeFraction(float dayTimeFraction) { + // No-op for rendering world + } + + @Override + public float getDayTimeFraction() { + return 0.0f; + } + + @Override + public float getDayTimePerTick() { + return 0.0f; + } + + @Override + public void setDayTimePerTick(float dayTimePerTick) { + // No-op for rendering world + } +} diff --git a/neoforge-main/src/main/java/dev/compactmods/crafting/field/ActiveWorldFields.java b/neoforge-main/src/main/java/dev/compactmods/crafting/field/ActiveWorldFields.java index b63e0a19..8e70e815 100644 --- a/neoforge-main/src/main/java/dev/compactmods/crafting/field/ActiveWorldFields.java +++ b/neoforge-main/src/main/java/dev/compactmods/crafting/field/ActiveWorldFields.java @@ -3,10 +3,14 @@ import dev.compactmods.crafting.CompactCrafting; import dev.compactmods.crafting.api.field.IMiniaturizationField; import dev.compactmods.crafting.api.field.ITickingMiniaturizationField; +import dev.compactmods.crafting.data.NbtListCollector; import dev.compactmods.crafting.network.FieldDeactivatedPacket; import dev.compactmods.crafting.projector.ProjectorHelper; +import dev.compactmods.crafting.proxies.data.BaseFieldProxyEntity; import dev.compactmods.crafting.recipes.MiniaturizationRecipe; import net.minecraft.core.BlockPos; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; import net.minecraft.resources.ResourceKey; import net.minecraft.server.level.ServerLevel; import net.minecraft.world.level.ChunkPos; @@ -15,6 +19,7 @@ import org.jetbrains.annotations.NotNull; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Optional; @@ -26,10 +31,11 @@ public class ActiveWorldFields { private final Level level; - /** - * Holds a set of miniaturization fields that are active, referenced by their center point. - */ private final HashMap> fields = new HashMap<>(); + private final HashMap> pendingFields = new HashMap<>(); + private final Set disconnectedProxies = new HashSet<>(); + private final Map proxyToFieldMap = new HashMap<>(); + private int retryTicker = 0; private ActiveWorldFields(Level level) { this.level = level; @@ -44,6 +50,12 @@ public Stream> getFields() { } public void tickFields() { + retryTicker++; + + if (retryTicker % 20 == 0 && !pendingFields.isEmpty()) { + retryPendingFields(); + } + Set loaded = fields.values().stream() .filter(IMiniaturizationField::isAreaLoaded) .filter(field -> field instanceof ITickingMiniaturizationField) @@ -56,19 +68,50 @@ public void tickFields() { CompactCrafting.LOGGER.trace("Loaded count ({}): {}", level.dimension().location(), loaded.size()); loaded.forEach(ITickingMiniaturizationField::tick); } + + private void retryPendingFields() { + var iterator = pendingFields.entrySet().iterator(); + while (iterator.hasNext()) { + var entry = iterator.next(); + BlockPos center = entry.getKey(); + IMiniaturizationField field = entry.getValue(); + + final Optional anyMissing = ProjectorHelper + .getMissingProjectors(level, field.getFieldSize(), field.getCenter()) + .findFirst(); + + if (anyMissing.isEmpty()) { + addFieldInstance(field); + iterator.remove(); + CompactCrafting.LOGGER.debug("Successfully registered delayed field at center {}", center); + } + } + + retryProxyConnections(); + } + + private void retryProxyConnections() { + var iterator = disconnectedProxies.iterator(); + while (iterator.hasNext()) { + var proxy = iterator.next(); + if (proxy.isRemoved()) { + iterator.remove(); + continue; + } + + if (proxy.tryReconnectToField()) { + iterator.remove(); + } + } + } + + public void registerDisconnectedProxy(BaseFieldProxyEntity proxy) { + disconnectedProxies.add(proxy); + } public void addFieldInstance(IMiniaturizationField field) { BlockPos center = field.getCenter(); fields.put(center, field); - - // TODO: Attachment for field invalidation -// LazyOptional lazy = LazyOptional.of(() -> field); -// laziness.put(center, lazy); -// field.setRef(lazy); -// -// lazy.addListener(lo -> { -// lo.ifPresent(this::unregisterField); -// }); } public IMiniaturizationField registerField(IMiniaturizationField field) { @@ -77,41 +120,29 @@ public IMiniaturizationField registerField(IMiniaturizati .findFirst(); if (anyMissing.isPresent()) { - CompactCrafting.LOGGER.warn("Trying to register an active field with missing projector at {}; real state: {}", anyMissing.get(), level.getBlockState(anyMissing.get())); + BlockPos center = field.getCenter(); + if (!pendingFields.containsKey(center)) { + pendingFields.put(center, field); + CompactCrafting.LOGGER.debug("Field registration delayed for center {} - projector at {} not ready yet", center, anyMissing.get()); + } return field; } addFieldInstance(field); - - // FIXME - Set projector back-references to field - // field.getProjectors().locations().forEach(pos -> { -// BlockState stateAt = level.getBlockState(pos); -// if (!(stateAt.getBlock() instanceof FieldProjectorBlock)) -// return; -// -// if (stateAt.hasBlockEntity()) { -// BlockEntity tileAt = level.getBlockEntity(pos); -// if (tileAt instanceof FieldProjectorEntity) { -// // ((FieldProjectorEntity) tileAt).setFieldRef(field.getRef()); -// } -// } -// }); - return field; } public void unregisterField(BlockPos center) { if (fields.containsKey(center)) { var removedField = fields.remove(center); -// final LazyOptional removed = laziness.remove(center); -// removed.invalidate(); if (!level.isClientSide && removedField != null && level instanceof ServerLevel sl) { - // Send deactivation packet to clients PacketDistributor.sendToPlayersTrackingChunk(sl, new ChunkPos(removedField.getCenter()), new FieldDeactivatedPacket(removedField.getFieldSize(), removedField.getCenter(), List.copyOf(removedField.getProjectors().locations()))); } } + + pendingFields.remove(center); } public void unregisterField(IMiniaturizationField field) { @@ -138,22 +169,23 @@ public ResourceKey getLevel() { return level.dimension(); } + public ListTag serializeNBT() { + return getFields() + .map(field -> { + if (field instanceof MiniaturizationField mf) { + return mf.serverData(); + } + return new CompoundTag(); + }) + .collect(NbtListCollector.toNbtList()); + } - -// public ListTag serializeNBT() { -// return getFields() -// .map(IMiniaturizationField::serverData) -// .collect(NbtListCollector.toNbtList()); -// } -// - - -// public void deserializeNBT(ListTag nbt) { -// nbt.forEach(item -> { -// if (item instanceof CompoundTag ct) { -// MiniaturizationField field = new MiniaturizationField(ct); -// addFieldInstance(field); -// } -// }); -// } + public void deserializeNBT(ListTag nbt) { + nbt.forEach(item -> { + if (item instanceof CompoundTag ct) { + MiniaturizationField field = MiniaturizationField.fromNBT(level, ct); + addFieldInstance(field); + } + }); + } } diff --git a/neoforge-main/src/main/java/dev/compactmods/crafting/field/MiniaturizationField.java b/neoforge-main/src/main/java/dev/compactmods/crafting/field/MiniaturizationField.java index c83208e7..4450d24b 100644 --- a/neoforge-main/src/main/java/dev/compactmods/crafting/field/MiniaturizationField.java +++ b/neoforge-main/src/main/java/dev/compactmods/crafting/field/MiniaturizationField.java @@ -2,6 +2,7 @@ import dev.compactmods.crafting.CompactCrafting; import dev.compactmods.crafting.api.EnumCraftingState; +import dev.compactmods.crafting.api.field.IFieldListener; import dev.compactmods.crafting.api.field.IMiniaturizationField; import dev.compactmods.crafting.api.field.ITickingMiniaturizationField; import dev.compactmods.crafting.api.field.MiniaturizationFieldSize; @@ -10,6 +11,7 @@ import dev.compactmods.crafting.core.CCMiniaturizationRecipes; import dev.compactmods.crafting.crafting.CraftingHelper; import dev.compactmods.crafting.events.WorldEventHandler; +import dev.compactmods.crafting.network.FieldActivatedPacket; import dev.compactmods.crafting.network.FieldDeactivatedPacket; import dev.compactmods.crafting.network.FieldRecipeChangedPacket; import dev.compactmods.crafting.recipes.MiniaturizationRecipe; @@ -22,6 +24,10 @@ import net.minecraft.core.Direction; import net.minecraft.core.Vec3i; import net.minecraft.core.particles.DustParticleOptions; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.NbtUtils; +import net.minecraft.resources.ResourceLocation; import net.minecraft.core.particles.ParticleOptions; import net.minecraft.core.particles.ParticleTypes; import net.minecraft.server.level.ServerLevel; @@ -44,9 +50,11 @@ import java.lang.ref.WeakReference; import java.util.Comparator; +import java.util.HashSet; import java.util.List; import java.util.Optional; import java.util.Set; +import java.util.concurrent.CopyOnWriteArraySet; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -72,6 +80,9 @@ public class MiniaturizationField implements IMiniaturizationField listeners = new CopyOnWriteArraySet<>(); private static Disposable CHUNK_LISTENER; @@ -91,30 +102,51 @@ public MiniaturizationField(Level level, MiniaturizationFieldSize size, BlockPos setupChunkListener(); } -// public MiniaturizationField(CompoundTag nbt) { -// this.craftingState = EnumCraftingState.valueOf(nbt.getString("state")); -// -// this.center = NbtUtils.readBlockPos(nbt, "center").orElseThrow(); -// this.size = MiniaturizationFieldSize.valueOf(nbt.getString("size")); -// -// setupChunkListener(); -// -// // temp load recipe -// if (nbt.contains("recipe")) { -// this.recipeId = ResourceLocation.parse(nbt.getString("recipe")); -// this.craftingProgress = nbt.getInt("progress"); -// } -// -// if (nbt.contains("matchedBlocks")) { -// StructureTemplate t = new StructureTemplate(); -// t.load(BuiltInRegistries.BLOCK.asLookup(), nbt.getCompound("matchedBlocks")); -// this.matchedBlocks = t; -// } else { -// this.matchedBlocks = null; -// } -// -// this.disabled = nbt.contains("disabled") && nbt.getBoolean("disabled"); -// } + private MiniaturizationField(Level level, CompoundTag nbt) { + this.level = level; + this.craftingState = EnumCraftingState.valueOf(nbt.getString("state")); + + this.center = NbtUtils.readBlockPos(nbt, "center").orElseThrow(); + this.size = MiniaturizationFieldSize.valueOf(nbt.getString("size")); + this.projectors = new FieldProjectorSet(new WeakReference<>(level), this.size.getProjectorLocations(this.center).collect(Collectors.toSet()), this.size); + + if (level != null) { + setupChunkListener(); + } + + // temp load recipe + if (nbt.contains("recipe") && level != null) { + ResourceLocation recipeId = ResourceLocation.parse(nbt.getString("recipe")); + this.craftingProgress = nbt.getInt("progress"); + + // Load recipe from registry + level.getRecipeManager().byKey(recipeId).ifPresent(recipe -> { + if (recipe.value() instanceof MiniaturizationRecipe mr) { + this.currentRecipe = (RecipeHolder) recipe; + } + }); + } + + // if we're in CRAFTING/MATCHED state but have no recipe, reset to NOT_MATCHED + if ((this.craftingState == EnumCraftingState.CRAFTING || this.craftingState == EnumCraftingState.MATCHED) && this.currentRecipe == null) { + this.craftingState = EnumCraftingState.NOT_MATCHED; + this.craftingProgress = 0; + } + + if (nbt.contains("matchedBlocks")) { + StructureTemplate t = new StructureTemplate(); + t.load(BuiltInRegistries.BLOCK.asLookup(), nbt.getCompound("matchedBlocks")); + this.matchedBlocks = t; + } else { + this.matchedBlocks = null; + } + + this.disabled = nbt.contains("disabled") && nbt.getBoolean("disabled"); + } + + public static MiniaturizationField fromNBT(Level level, CompoundTag nbt) { + return new MiniaturizationField(level, nbt); + } private void setupChunkListener() { // add projector and central chunks @@ -218,10 +250,10 @@ public void setRecipe(RecipeHolder recipe) { if (craftingState == EnumCraftingState.NOT_MATCHED) setCraftingState(EnumCraftingState.MATCHED); -// this.listeners.forEach(li -> li.ifPresent(l -> { -// l.onRecipeChanged(this, this.currentRecipe); -// l.onRecipeMatched(this, this.currentRecipe); -// })); + this.listeners.forEach(l -> { + l.onRecipeChanged(this, this.currentRecipe.value()); + l.onRecipeMatched(this, this.currentRecipe.value()); + }); } @Override @@ -230,10 +262,10 @@ public void clearRecipe() { this.craftingProgress = 0; setCraftingState(EnumCraftingState.NOT_MATCHED); -// listeners.forEach(l -> l.ifPresent(listener -> { -// listener.onRecipeChanged(this, this.currentRecipe); -// listener.onRecipeCleared(this); -// })); + listeners.forEach(listener -> { + listener.onRecipeChanged(this, null); + listener.onRecipeCleared(this); + }); } @Override @@ -254,6 +286,11 @@ public void tick() { this.rescanTime = 0; break; } + + // Rescan every 5 seconds because tools like the Advanced Swapper don't trigger the field contents changed + if (level.getGameTime() % 100 == 0) { + doRecipeScan(); + } break; case MATCHED: @@ -314,7 +351,7 @@ private void tickCrafting() { IMiniaturizationRecipe completed = this.currentRecipe.value(); clearRecipe(); -// listeners.forEach(l -> l.ifPresent(listener -> listener.onRecipeCompleted(this, completed))); + listeners.forEach(listener -> listener.onRecipeCompleted(this, completed)); } } @@ -412,12 +449,12 @@ public void doRecipeScan() { // Update all listeners as well final var finalMatchedRecipe = this.currentRecipe; -// listeners.forEach(l -> l.ifPresent(fl -> { -// fl.onRecipeChanged(this, finalMatchedRecipe); -// -// if (craftingState == EnumCraftingState.MATCHED) -// fl.onRecipeMatched(this, finalMatchedRecipe); -// })); + listeners.forEach(fl -> { + fl.onRecipeChanged(this, finalMatchedRecipe != null ? finalMatchedRecipe.value() : null); + + if (craftingState == EnumCraftingState.MATCHED && finalMatchedRecipe != null) + fl.onRecipeMatched(this, finalMatchedRecipe.value()); + }); } @Override @@ -441,7 +478,7 @@ public void checkLoaded() { this.areaLoaded = level.isAreaLoaded(center, size.getProjectorDistance() + 3); if (areaLoaded) { -// listeners.forEach(l -> l.ifPresent(fl -> fl.onFieldActivated(this))); + listeners.forEach(fl -> fl.onFieldActivated(this)); } } @@ -453,6 +490,16 @@ public void fieldContentsChanged() { // set a distant rescan duration to make the field revalidate itself after a second or two this.rescanTime = level.getGameTime() + 30; } + + public void registerListener(IFieldListener listener) { + this.listeners.add(listener); + CompactCrafting.LOGGER.debug("Registered field listener: {}", listener); + } + + public void unregisterListener(IFieldListener listener) { + this.listeners.remove(listener); + CompactCrafting.LOGGER.debug("Unregistered field listener: {}", listener); + } // @Override // public void registerListener(LazyOptional listener) { @@ -562,9 +609,8 @@ public void enable() { this.projectors.enableAll(); if (this.level instanceof ServerLevel sl) { - // FIXME - // FieldDeactivatedPacket update = new FieldActivatedPacket(this, this.clientData()); - // PacketDistributor.sendToPlayersTrackingChunk(sl, new ChunkPos(center), update); + FieldActivatedPacket update = new FieldActivatedPacket(this, this.serverData()); + PacketDistributor.sendToPlayersTrackingChunk(sl, new ChunkPos(center), update); } } @@ -591,10 +637,30 @@ public RecipeHolder recipeHolder() { @Override public MiniaturizationRecipe currentRecipe() { - return currentRecipe.value(); + return currentRecipe != null ? currentRecipe.value() : null; + } + + public CompoundTag serverData() { + CompoundTag nbt = new CompoundTag(); + nbt.putString("size", size.name()); + nbt.put("center", NbtUtils.writeBlockPos(center)); + + nbt.putString("state", craftingState.name()); + + if (currentRecipe != null) { + nbt.putString("recipe", currentRecipe.id().toString()); + nbt.putInt("progress", craftingProgress); + } + + if (matchedBlocks != null) { + nbt.put("matchedBlocks", matchedBlocks.save(new CompoundTag())); + } + + nbt.putBoolean("disabled", this.disabled); + + return nbt; } - // FIXME // @Override // public Tag serializeNBT() { // CompoundTag fieldInfo = new CompoundTag(); diff --git a/neoforge-main/src/main/java/dev/compactmods/crafting/field/render/CraftingPreviewRenderer.java b/neoforge-main/src/main/java/dev/compactmods/crafting/field/render/CraftingPreviewRenderer.java index 37177eb0..70c12ead 100644 --- a/neoforge-main/src/main/java/dev/compactmods/crafting/field/render/CraftingPreviewRenderer.java +++ b/neoforge-main/src/main/java/dev/compactmods/crafting/field/render/CraftingPreviewRenderer.java @@ -5,6 +5,8 @@ import dev.compactmods.crafting.api.components.IRecipeBlockComponent; import dev.compactmods.crafting.api.recipe.IMiniaturizationRecipe; import dev.compactmods.crafting.api.recipe.layers.IRecipeLayer; +import dev.compactmods.crafting.client.render.CCRenderTypes; +import dev.compactmods.crafting.client.render.GhostRenderer; import dev.compactmods.crafting.util.BlockSpaceUtil; import dev.compactmods.crafting.util.MathUtil; import net.minecraft.client.Minecraft; @@ -13,6 +15,9 @@ import net.minecraft.client.renderer.block.BlockRenderDispatcher; import net.minecraft.client.renderer.texture.OverlayTexture; import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.util.FastColor; +import net.minecraft.util.Mth; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.AABB; import net.neoforged.neoforge.client.model.data.ModelData; @@ -25,6 +30,10 @@ public static void render(IMiniaturizationRecipe recipe, double progress, PoseSt if(recipe == null) return; + + if(progress >= recipe.getCraftingTime()) { + return; + } stack.pushPose(); @@ -63,7 +72,7 @@ public static void render(IMiniaturizationRecipe recipe, double progress, PoseSt BlockPos zeroedPos = filledPos.below(finalY); l.getComponentForPosition(zeroedPos) .flatMap(recipe.getComponents()::getBlock) - .ifPresent(comp -> renderSingleBlock(stack, buffers, blockRenderer, comp)); + .ifPresent(comp -> renderSingleBlock(stack, buffers, blockRenderer, comp, craftProgress, recipe.getCraftingTime())); stack.popPose(); }); @@ -80,11 +89,62 @@ public static void render(IMiniaturizationRecipe recipe, double progress, PoseSt stack.popPose(); } - private static void renderSingleBlock(PoseStack stack, MultiBufferSource buffers, BlockRenderDispatcher blockRenderer, IRecipeBlockComponent comp) { - // TODO - Render switching - BlockState state1 = comp.getRenderState(); - - // TODO - Revisit render type - blockRenderer.renderSingleBlock(state1, stack, buffers, LightTexture.FULL_SKY, OverlayTexture.NO_OVERLAY, ModelData.EMPTY, null); + private static void renderSingleBlock(PoseStack stack, MultiBufferSource buffers, BlockRenderDispatcher blockRenderer, IRecipeBlockComponent comp, double progress, double craftingTime) { + BlockState state = comp.getRenderState(); + + double progressPercent = Math.min(progress / craftingTime, 1.0); + + stack.pushPose(); + + stack.translate(0.5, 0.5, 0.5); + + long gameTime = Minecraft.getInstance().level.getGameTime(); + double spinSpeed = 2.0d + (progressPercent * 8.0d); + double blockAngle = (gameTime % 360.0) * spinSpeed; + stack.mulPose(Axis.YP.rotationDegrees((float) blockAngle)); + + double individualScale = 1.0 - (progressPercent * 0.3); + stack.scale((float) individualScale, (float) individualScale, (float) individualScale); + + stack.translate(-0.5, -0.5, -0.5); + + final var mc = Minecraft.getInstance(); + final var colors = mc.getBlockColors(); + final var builder = buffers.getBuffer(CCRenderTypes.PHANTOM); + final var dispatcher = mc.getBlockRenderer(); + final var model = dispatcher.getBlockModel(state); + + if (model != mc.getModelManager().getMissingModel()) { + final float alpha = 0.9f; // 90% opaque (10% transparent) + + for (var dir : Direction.values()) { + model.getQuads(state, dir, mc.level.random, ModelData.EMPTY, null) + .forEach(quad -> { + int color = quad.isTinted() ? colors.getColor(state, mc.level, BlockPos.ZERO, quad.getTintIndex()) : + FastColor.ARGB32.color(255, 255, 255, 255); + + final float red = FastColor.ARGB32.red(color) / 255f; + final float green = FastColor.ARGB32.green(color) / 255f; + final float blue = FastColor.ARGB32.blue(color) / 255f; + + builder.putBulkData(stack.last(), quad, red, green, blue, alpha, + LightTexture.FULL_BRIGHT, OverlayTexture.NO_OVERLAY, false); + }); + } + + model.getQuads(state, null, mc.level.random, ModelData.EMPTY, null) + .forEach(quad -> { + int color = quad.isTinted() ? colors.getColor(state, mc.level, BlockPos.ZERO, quad.getTintIndex()) : + FastColor.ARGB32.color(255, 255, 255, 255); + + final float red = FastColor.ARGB32.red(color) / 255f; + final float green = FastColor.ARGB32.green(color) / 255f; + final float blue = FastColor.ARGB32.blue(color) / 255f; + + builder.putBulkData(stack.last(), quad, red, green, blue, alpha, + LightTexture.FULL_BRIGHT, OverlayTexture.NO_OVERLAY, false); + }); + } + stack.popPose(); } } diff --git a/neoforge-main/src/main/java/dev/compactmods/crafting/network/ClientFieldUnwatchPacket.java b/neoforge-main/src/main/java/dev/compactmods/crafting/network/ClientFieldUnwatchPacket.java index 385a6705..a31f031a 100644 --- a/neoforge-main/src/main/java/dev/compactmods/crafting/network/ClientFieldUnwatchPacket.java +++ b/neoforge-main/src/main/java/dev/compactmods/crafting/network/ClientFieldUnwatchPacket.java @@ -1,28 +1,33 @@ -//package dev.compactmods.crafting.network; -// -//import dev.compactmods.crafting.client.ClientPacketHandler; -//import net.minecraft.core.BlockPos; -//import net.minecraft.network.FriendlyByteBuf; -//import net.neoforged.neoforge.network.NetworkEvent; -// -//public class ClientFieldUnwatchPacket { -// -// private final BlockPos center; -// -// public ClientFieldUnwatchPacket(BlockPos center) { -// this.center = center; -// } -// -// public ClientFieldUnwatchPacket(FriendlyByteBuf buf) { -// this.center = buf.readBlockPos(); -// } -// -// public static void encode(ClientFieldUnwatchPacket pkt, FriendlyByteBuf buf) { -// buf.writeBlockPos(pkt.center); -// } -// -// public static boolean handle(ClientFieldUnwatchPacket pkt, NetworkEvent.Context context) { -// ClientPacketHandler.removeField(pkt.center); -// return true; -// } -//} +package dev.compactmods.crafting.network; + +import dev.compactmods.crafting.CompactCrafting; +import dev.compactmods.crafting.client.ClientPacketHandler; +import net.minecraft.core.BlockPos; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import net.neoforged.fml.loading.FMLEnvironment; +import net.neoforged.neoforge.network.handling.IPayloadHandler; + +public record ClientFieldUnwatchPacket(BlockPos center) implements CustomPacketPayload { + + public static final Type TYPE = new Type<>(CompactCrafting.modRL("field_unwatch")); + + public static final StreamCodec STREAM_CODEC = StreamCodec.composite( + BlockPos.STREAM_CODEC, ClientFieldUnwatchPacket::center, + ClientFieldUnwatchPacket::new + ); + + public static final IPayloadHandler HANDLER = (pkt, ctx) -> { + ctx.enqueueWork(() -> { + if (FMLEnvironment.dist.isClient()) { + ClientPacketHandler.removeField(pkt.center); + } + }); + }; + + @Override + public Type type() { + return TYPE; + } +} diff --git a/neoforge-main/src/main/java/dev/compactmods/crafting/network/ClientFieldWatchPacket.java b/neoforge-main/src/main/java/dev/compactmods/crafting/network/ClientFieldWatchPacket.java index 12f994cb..7f5a6254 100644 --- a/neoforge-main/src/main/java/dev/compactmods/crafting/network/ClientFieldWatchPacket.java +++ b/neoforge-main/src/main/java/dev/compactmods/crafting/network/ClientFieldWatchPacket.java @@ -1,31 +1,52 @@ -//package dev.compactmods.crafting.network; -// -//import dev.compactmods.crafting.api.field.IMiniaturizationField; -//import dev.compactmods.crafting.client.ClientPacketHandler; -//import dev.compactmods.crafting.field.MiniaturizationField; -//import net.minecraft.nbt.CompoundTag; -//import net.minecraft.network.FriendlyByteBuf; -// -//public class ClientFieldWatchPacket { -// -// private final IMiniaturizationField field; -// private final CompoundTag clientData; -// -// public ClientFieldWatchPacket(IMiniaturizationField field) { -// this.field = field; -// this.clientData = field.clientData(); -// } -// -// public ClientFieldWatchPacket(FriendlyByteBuf buf) { -// this.field = new MiniaturizationField(); -// this.clientData = buf.readNbt(); -// } -// -// public static void encode(ClientFieldWatchPacket pkt, FriendlyByteBuf buf) { -// buf.writeNbt(pkt.field.clientData()); -// } -// -// public static void handle(ClientFieldWatchPacket pkt) { -// ClientPacketHandler.handleFieldData(pkt.clientData); -// } -//} +package dev.compactmods.crafting.network; + +import dev.compactmods.crafting.CompactCrafting; +import dev.compactmods.crafting.api.field.IMiniaturizationField; +import dev.compactmods.crafting.api.field.MiniaturizationFieldSize; +import dev.compactmods.crafting.client.ClientPacketHandler; +import dev.compactmods.crafting.field.MiniaturizationField; +import dev.compactmods.crafting.recipes.MiniaturizationRecipe; +import net.minecraft.core.BlockPos; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.codec.ByteBufCodecs; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import net.neoforged.fml.loading.FMLEnvironment; +import net.neoforged.neoforge.network.handling.IPayloadHandler; + +public record ClientFieldWatchPacket(MiniaturizationFieldSize fieldSize, BlockPos center, CompoundTag clientData) implements CustomPacketPayload { + + public static final Type TYPE = new Type<>(CompactCrafting.modRL("field_watch")); + + public ClientFieldWatchPacket(IMiniaturizationField field) { + this(field.getFieldSize(), field.getCenter(), getClientData(field)); + } + + private static CompoundTag getClientData(IMiniaturizationField field) { + if (field instanceof MiniaturizationField mf) { + return mf.serverData(); + } + return new CompoundTag(); + } + + public static final StreamCodec STREAM_CODEC = StreamCodec.composite( + MiniaturizationFieldSize.STREAM_CODEC, ClientFieldWatchPacket::fieldSize, + BlockPos.STREAM_CODEC, ClientFieldWatchPacket::center, + ByteBufCodecs.COMPOUND_TAG, ClientFieldWatchPacket::clientData, + ClientFieldWatchPacket::new + ); + + public static final IPayloadHandler HANDLER = (pkt, ctx) -> { + ctx.enqueueWork(() -> { + if (FMLEnvironment.dist.isClient()) { + ClientPacketHandler.handleFieldData(pkt.clientData); + } + }); + }; + + @Override + public Type type() { + return TYPE; + } +} diff --git a/neoforge-main/src/main/java/dev/compactmods/crafting/network/NetworkHandler.java b/neoforge-main/src/main/java/dev/compactmods/crafting/network/NetworkHandler.java index 8579d60c..3c7643a2 100644 --- a/neoforge-main/src/main/java/dev/compactmods/crafting/network/NetworkHandler.java +++ b/neoforge-main/src/main/java/dev/compactmods/crafting/network/NetworkHandler.java @@ -11,9 +11,11 @@ public static void onPacketRegistration(final RegisterPayloadHandlersEvent paylo main.playToClient(FieldDeactivatedPacket.TYPE, FieldDeactivatedPacket.STREAM_CODEC, FieldDeactivatedPacket.HANDLER); main.playToClient(FieldActivatedPacket.TYPE, FieldActivatedPacket.STREAM_CODEC, FieldActivatedPacket.HANDLER); -// main.playToClient(ClientFieldWatchPacket.TYPE, ClientFieldWatchPacket.CODEC, ClientFieldWatchPacket.HANDLER); -// main.playToClient(ClientFieldUnwatchPacket.TYPE, ClientFieldUnwatchPacket.CODEC, ClientFieldUnwatchPacket.HANDLER); + main.playToClient(ClientFieldWatchPacket.TYPE, ClientFieldWatchPacket.STREAM_CODEC, ClientFieldWatchPacket.HANDLER); + main.playToClient(ClientFieldUnwatchPacket.TYPE, ClientFieldUnwatchPacket.STREAM_CODEC, ClientFieldUnwatchPacket.HANDLER); main.playToClient(FieldRecipeChangedPacket.TYPE, FieldRecipeChangedPacket.STREAM_CODEC, FieldRecipeChangedPacket.HANDLER); + main.playToServer(RequestProxyDataPacket.TYPE, RequestProxyDataPacket.STREAM_CODEC, RequestProxyDataPacket::handle); + main.playToClient(ProxyDataResponsePacket.TYPE, ProxyDataResponsePacket.STREAM_CODEC, ProxyDataResponsePacket::handle); } } diff --git a/neoforge-main/src/main/java/dev/compactmods/crafting/network/ProxyDataResponsePacket.java b/neoforge-main/src/main/java/dev/compactmods/crafting/network/ProxyDataResponsePacket.java new file mode 100644 index 00000000..e35db45b --- /dev/null +++ b/neoforge-main/src/main/java/dev/compactmods/crafting/network/ProxyDataResponsePacket.java @@ -0,0 +1,46 @@ +package dev.compactmods.crafting.network; + +import dev.compactmods.crafting.CompactCrafting; +import dev.compactmods.crafting.client.ClientPacketHandler; +import net.minecraft.core.BlockPos; +import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import net.neoforged.neoforge.network.handling.IPayloadContext; + +import java.util.UUID; +import org.jetbrains.annotations.Nullable; + +public record ProxyDataResponsePacket(BlockPos proxyPos, @Nullable BlockPos fieldCenter, UUID proxyId) implements CustomPacketPayload { + + public static final CustomPacketPayload.Type TYPE = new CustomPacketPayload.Type<>(CompactCrafting.modRL("proxy_data_response")); + + public static final StreamCodec STREAM_CODEC = new StreamCodec() { + @Override + public ProxyDataResponsePacket decode(RegistryFriendlyByteBuf buf) { + BlockPos proxyPos = BlockPos.STREAM_CODEC.decode(buf); + BlockPos fieldCenter = buf.readBoolean() ? BlockPos.STREAM_CODEC.decode(buf) : null; + UUID proxyId = buf.readUUID(); + return new ProxyDataResponsePacket(proxyPos, fieldCenter, proxyId); + } + + @Override + public void encode(RegistryFriendlyByteBuf buf, ProxyDataResponsePacket packet) { + BlockPos.STREAM_CODEC.encode(buf, packet.proxyPos); + buf.writeBoolean(packet.fieldCenter != null); + if (packet.fieldCenter != null) { + BlockPos.STREAM_CODEC.encode(buf, packet.fieldCenter); + } + buf.writeUUID(packet.proxyId); + } + }; + + @Override + public Type type() { + return TYPE; + } + + public static void handle(ProxyDataResponsePacket packet, IPayloadContext context) { + ClientPacketHandler.handleProxyData(packet.proxyPos, packet.fieldCenter, packet.proxyId); + } +} \ No newline at end of file diff --git a/neoforge-main/src/main/java/dev/compactmods/crafting/network/RequestProxyDataPacket.java b/neoforge-main/src/main/java/dev/compactmods/crafting/network/RequestProxyDataPacket.java new file mode 100644 index 00000000..c154b27d --- /dev/null +++ b/neoforge-main/src/main/java/dev/compactmods/crafting/network/RequestProxyDataPacket.java @@ -0,0 +1,35 @@ +package dev.compactmods.crafting.network; + +import dev.compactmods.crafting.CompactCrafting; +import dev.compactmods.crafting.proxies.data.BaseFieldProxyEntity; +import net.minecraft.core.BlockPos; +import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import net.minecraft.server.level.ServerPlayer; +import net.neoforged.neoforge.network.PacketDistributor; +import net.neoforged.neoforge.network.handling.IPayloadContext; + +public record RequestProxyDataPacket(BlockPos proxyPos) implements CustomPacketPayload { + + public static final CustomPacketPayload.Type TYPE = new CustomPacketPayload.Type<>(CompactCrafting.modRL("request_proxy_data")); + + public static final StreamCodec STREAM_CODEC = StreamCodec.composite( + BlockPos.STREAM_CODEC, RequestProxyDataPacket::proxyPos, + RequestProxyDataPacket::new + ); + + @Override + public Type type() { + return TYPE; + } + + public static void handle(RequestProxyDataPacket packet, IPayloadContext context) { + if (context.player() instanceof ServerPlayer serverPlayer) { + var blockEntity = serverPlayer.level().getBlockEntity(packet.proxyPos); + if (blockEntity instanceof BaseFieldProxyEntity proxy) { + PacketDistributor.sendToPlayer(serverPlayer, new ProxyDataResponsePacket(packet.proxyPos, proxy.fieldCenter, proxy.getProxyId())); + } + } + } +} \ No newline at end of file diff --git a/neoforge-main/src/main/java/dev/compactmods/crafting/projector/FieldProjectorBlock.java b/neoforge-main/src/main/java/dev/compactmods/crafting/projector/FieldProjectorBlock.java index d32191c7..0122551f 100644 --- a/neoforge-main/src/main/java/dev/compactmods/crafting/projector/FieldProjectorBlock.java +++ b/neoforge-main/src/main/java/dev/compactmods/crafting/projector/FieldProjectorBlock.java @@ -6,6 +6,7 @@ import dev.compactmods.crafting.api.projector.FieldProjectorProperties; import dev.compactmods.crafting.client.render.GhostProjectorPlacementRenderer; import dev.compactmods.crafting.data.CCAttachments; +import dev.compactmods.crafting.field.IMutableMiniaturizationField; import dev.compactmods.crafting.field.MiniaturizationField; import dev.compactmods.crafting.network.FieldActivatedPacket; import dev.compactmods.crafting.recipes.MiniaturizationRecipe; @@ -220,8 +221,9 @@ public void onPlace(BlockState state, Level level, BlockPos pos, BlockState oldS field.fieldContentsChanged(); // Send activation packet to clients + CompoundTag fieldData = field instanceof MiniaturizationField mf ? mf.serverData() : new CompoundTag(); PacketDistributor.sendToPlayersTrackingChunk(sl, new ChunkPos(field.getCenter()), - new FieldActivatedPacket(field, new CompoundTag())); + new FieldActivatedPacket(field, fieldData)); } } } @@ -259,28 +261,34 @@ public void neighborChanged(BlockState state, Level level, BlockPos pos, Block c if (level.isClientSide) return; - // FIXME REDSTONE HANDLING + // Redstone handling if (isActive(state)) { CompactCrafting.LOGGER.debug("redstone check!"); -// BlockEntity tile = level.getBlockEntity(pos); -// if (tile instanceof FieldProjectorEntity) { -// FieldProjectorEntity fpt = (FieldProjectorEntity) tile; -// if (level.getBestNeighborSignal(pos) > 0) { -// // receiving power from some side, turn off rendering -// fpt.getField().ifPresent(IMiniaturizationField::disable); -// } else { -// // check other projectors, if there's a redstone signal anywhere, we disable the field -// fpt.getField().ifPresent(IMiniaturizationField::checkRedstone); -// } -// } -// } else { -// // not active, but we may be re-enabling a disabled field -// ProjectorHelper.getClosestOppositeSize(level, pos).ifPresent(size -> { -// final BlockPos center = size.getCenterFromProjector(pos, state.getValue(FACING)); -// level.getCapability(CCCapabilities.FIELDS).ifPresent(fields -> { -// fields.get(center).ifPresent(IMiniaturizationField::checkRedstone); -// }); -// }); + BlockEntity tile = level.getBlockEntity(pos); + if (tile instanceof FieldProjectorEntity fpt) { + // Find the field this projector belongs to + ProjectorHelper.getClosestOppositeSize(level, pos).ifPresent(size -> { + final BlockPos center = size.getCenterFromProjector(pos, state.getValue(FACING)); + level.getData(CCAttachments.ACTIVE_FIELDS).get(center).ifPresent(field -> { + if (level.getBestNeighborSignal(pos) > 0) { + // receiving power from some side, turn off rendering + if (field instanceof IMutableMiniaturizationField mutable) { + mutable.disable(); + } + } else { + // check other projectors, if there's a redstone signal anywhere, we disable the field + field.checkRedstone(); + } + }); + }); + } + } else { + // not active, but we may be re-enabling a disabled field + ProjectorHelper.getClosestOppositeSize(level, pos).ifPresent(size -> { + final BlockPos center = size.getCenterFromProjector(pos, state.getValue(FACING)); + level.getData(CCAttachments.ACTIVE_FIELDS).get(center) + .ifPresent(IMiniaturizationField::checkRedstone); + }); } } diff --git a/neoforge-main/gtfo/proxies/ProxyMode.java b/neoforge-main/src/main/java/dev/compactmods/crafting/proxies/ProxyMode.java similarity index 100% rename from neoforge-main/gtfo/proxies/ProxyMode.java rename to neoforge-main/src/main/java/dev/compactmods/crafting/proxies/ProxyMode.java diff --git a/neoforge-main/gtfo/proxies/block/FieldProxyBlock.java b/neoforge-main/src/main/java/dev/compactmods/crafting/proxies/block/FieldProxyBlock.java similarity index 64% rename from neoforge-main/gtfo/proxies/block/FieldProxyBlock.java rename to neoforge-main/src/main/java/dev/compactmods/crafting/proxies/block/FieldProxyBlock.java index aa65f018..fb26dd9b 100644 --- a/neoforge-main/gtfo/proxies/block/FieldProxyBlock.java +++ b/neoforge-main/src/main/java/dev/compactmods/crafting/proxies/block/FieldProxyBlock.java @@ -1,11 +1,13 @@ package dev.compactmods.crafting.proxies.block; +import dev.compactmods.crafting.CompactCrafting; +import net.neoforged.fml.loading.FMLLoader; import org.jetbrains.annotations.Nullable; +import dev.compactmods.crafting.client.ClientPacketHandler; +import dev.compactmods.crafting.core.CCDataComponents; import dev.compactmods.crafting.proxies.data.BaseFieldProxyEntity; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.NbtUtils; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.BlockGetter; @@ -28,6 +30,7 @@ public abstract class FieldProxyBlock extends Block { public FieldProxyBlock(Properties props) { super(props); + this.registerDefaultState(this.stateDefinition.any().setValue(SIGNAL, 0)); } @Override @@ -47,41 +50,19 @@ protected void createBlockStateDefinition(StateDefinition.Builder { -// CompoundTag fieldInfo = stack.getOrCreateTagElement("field"); -// fieldInfo.put("center", NbtUtils.writeBlockPos(field.getCenter())); -// }); -// } -// -// return stack; -// } - @Override public void setPlacedBy(Level level, BlockPos placedAt, BlockState state, @Nullable LivingEntity entity, ItemStack stack) { super.setPlacedBy(level, placedAt, state, entity, stack); - BaseFieldProxyEntity tile = (BaseFieldProxyEntity) level.getBlockEntity(placedAt); - - if (stack.hasTag()) { - CompoundTag nbt = stack.getTag(); + if (level.isClientSide) { + ClientPacketHandler.removeProxyData(placedAt); + } - if (nbt != null && nbt.contains("field")) { - CompoundTag fieldData = nbt.getCompound("field"); - if (fieldData.contains("center")) { - BlockPos center = NbtUtils.readBlockPos(fieldData.getCompound("center")); + BaseFieldProxyEntity tile = (BaseFieldProxyEntity) level.getBlockEntity(placedAt); - if (tile != null) - tile.updateField(center); - } - } + var fieldCenter = stack.get(CCDataComponents.FIELD_CENTER.get()); + if (fieldCenter != null && tile != null) { + tile.updateField(fieldCenter.center()); } } @@ -89,4 +70,13 @@ public void setPlacedBy(Level level, BlockPos placedAt, BlockState state, @Nulla public boolean canConnectRedstone(BlockState state, BlockGetter world, BlockPos pos, @Nullable Direction side) { return true; } -} + + @Override + public void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean isMoving) { + if (level.isClientSide && !state.is(newState.getBlock())) { + ClientPacketHandler.removeProxyData(pos); + if(!FMLLoader.isProduction())CompactCrafting.LOGGER.debug("Removed proxy data for {}", pos); + } + super.onRemove(state, level, pos, newState, isMoving); + } +} \ No newline at end of file diff --git a/neoforge-main/gtfo/proxies/block/MatchFieldProxyBlock.java b/neoforge-main/src/main/java/dev/compactmods/crafting/proxies/block/MatchFieldProxyBlock.java similarity index 77% rename from neoforge-main/gtfo/proxies/block/MatchFieldProxyBlock.java rename to neoforge-main/src/main/java/dev/compactmods/crafting/proxies/block/MatchFieldProxyBlock.java index d466f7c8..5d020004 100644 --- a/neoforge-main/gtfo/proxies/block/MatchFieldProxyBlock.java +++ b/neoforge-main/src/main/java/dev/compactmods/crafting/proxies/block/MatchFieldProxyBlock.java @@ -1,7 +1,7 @@ package dev.compactmods.crafting.proxies.block; import org.jetbrains.annotations.Nullable; -import dev.compactmods.crafting.core.CCCapabilities; +import dev.compactmods.crafting.data.CCAttachments; import dev.compactmods.crafting.proxies.data.MatchFieldProxyEntity; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; @@ -23,12 +23,12 @@ public void onPlace(BlockState currState, Level level, BlockPos placedAt, BlockS super.onPlace(currState, level, placedAt, prevState, update); MatchFieldProxyEntity tile = (MatchFieldProxyEntity) level.getBlockEntity(placedAt); - if (tile != null) { - tile.getCapability(CCCapabilities.MINIATURIZATION_FIELD) - .ifPresent(field -> { - int signal = field.getCurrentRecipe().isPresent() ? 15 : 0; - level.setBlock(placedAt, currState.setValue(SIGNAL, signal), Block.UPDATE_ALL); - }); + if (tile != null && tile.fieldCenter != null && !level.isClientSide) { + var fields = level.getData(CCAttachments.ACTIVE_FIELDS); + fields.get(tile.fieldCenter).ifPresent(field -> { + int signal = field.currentRecipe() != null ? 15 : 0; + level.setBlock(placedAt, currState.setValue(SIGNAL, signal), Block.UPDATE_ALL); + }); } } @@ -52,4 +52,5 @@ public int getSignal(BlockState state, BlockGetter level, BlockPos pos, Directio public BlockEntity newBlockEntity(BlockPos pos, BlockState state) { return new MatchFieldProxyEntity(pos, state); } -} + +} \ No newline at end of file diff --git a/neoforge-main/gtfo/proxies/block/RescanFieldProxyBlock.java b/neoforge-main/src/main/java/dev/compactmods/crafting/proxies/block/RescanFieldProxyBlock.java similarity index 76% rename from neoforge-main/gtfo/proxies/block/RescanFieldProxyBlock.java rename to neoforge-main/src/main/java/dev/compactmods/crafting/proxies/block/RescanFieldProxyBlock.java index de668a37..26148e41 100644 --- a/neoforge-main/gtfo/proxies/block/RescanFieldProxyBlock.java +++ b/neoforge-main/src/main/java/dev/compactmods/crafting/proxies/block/RescanFieldProxyBlock.java @@ -2,7 +2,7 @@ import org.jetbrains.annotations.Nullable; import dev.compactmods.crafting.api.EnumCraftingState; -import dev.compactmods.crafting.core.CCCapabilities; +import dev.compactmods.crafting.data.CCAttachments; import dev.compactmods.crafting.proxies.data.BaseFieldProxyEntity; import dev.compactmods.crafting.proxies.data.RescanFieldProxyEntity; import net.minecraft.core.BlockPos; @@ -33,13 +33,13 @@ public void neighborChanged(BlockState thisState, Level level, BlockPos thisPos, if (level.hasNeighborSignal(thisPos)) { // call recipe scan - if (tile != null) { - tile.getCapability(CCCapabilities.MINIATURIZATION_FIELD) - .ifPresent(field -> { - if(field.getCraftingState() != EnumCraftingState.CRAFTING) - field.fieldContentsChanged(); - }); + if (tile != null && tile.fieldCenter != null) { + var fields = level.getData(CCAttachments.ACTIVE_FIELDS); + fields.get(tile.fieldCenter).ifPresent(field -> { + if(field.getCraftingState() != EnumCraftingState.CRAFTING) + field.fieldContentsChanged(); + }); } } } -} +} \ No newline at end of file diff --git a/neoforge-main/src/main/java/dev/compactmods/crafting/proxies/data/BaseFieldProxyEntity.java b/neoforge-main/src/main/java/dev/compactmods/crafting/proxies/data/BaseFieldProxyEntity.java new file mode 100644 index 00000000..b30d3fba --- /dev/null +++ b/neoforge-main/src/main/java/dev/compactmods/crafting/proxies/data/BaseFieldProxyEntity.java @@ -0,0 +1,120 @@ +package dev.compactmods.crafting.proxies.data; + +import dev.compactmods.crafting.api.field.IMiniaturizationField; +import dev.compactmods.crafting.data.CCAttachments; +import dev.compactmods.crafting.recipes.MiniaturizationRecipe; +import net.minecraft.core.BlockPos; +import net.minecraft.core.HolderLookup; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.NbtUtils; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.entity.BlockEntityType; +import net.minecraft.world.level.block.state.BlockState; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Optional; +import java.util.UUID; + +public abstract class BaseFieldProxyEntity extends BlockEntity { + + @Nullable + public BlockPos fieldCenter; + + @Nullable + protected IMiniaturizationField field = null; + + private UUID proxyId = UUID.randomUUID(); + + public BaseFieldProxyEntity(BlockEntityType type, BlockPos pos, BlockState state) { + super(type, pos, state); + } + + @Override + public void onLoad() { + super.onLoad(); + + if(fieldCenter != null && level != null) { + reconnectToField(); + } + } + + private void reconnectToField() { + if (fieldCenter == null || level == null) return; + + var fields = level.getData(CCAttachments.ACTIVE_FIELDS); + var fieldOpt = fields.get(fieldCenter); + + if (fieldOpt.isPresent()) { + fieldChanged(fieldOpt.get()); + } else { + fields.registerDisconnectedProxy(this); + } + } + + public boolean tryReconnectToField() { + if (field == null && fieldCenter != null) { + var fields = level.getData(CCAttachments.ACTIVE_FIELDS); + var fieldOpt = fields.get(fieldCenter); + + if (fieldOpt.isPresent()) { + fieldChanged(fieldOpt.get()); + return true; + } + } + return false; + } + + public void updateField(BlockPos fieldCenter) { + if (level == null) + return; + + if(fieldCenter == null) { + this.field = null; + this.fieldCenter = null; + return; + } + + var fields = level.getData(CCAttachments.ACTIVE_FIELDS); + fields.get(fieldCenter).ifPresent(f -> { + this.fieldCenter = fieldCenter; + fieldChanged(f); + }); + + setChanged(); + } + + protected void fieldChanged(IMiniaturizationField f) { + this.field = f; + } + + public Optional> getField() { + return Optional.ofNullable(field); + } + + @Override + protected void saveAdditional(@NotNull CompoundTag tag, HolderLookup.Provider registries) { + super.saveAdditional(tag, registries); + + if(this.fieldCenter != null) { + tag.put("center", NbtUtils.writeBlockPos(this.fieldCenter)); + } + tag.putUUID("proxyId", proxyId); + } + + @Override + protected void loadAdditional(CompoundTag tag, HolderLookup.Provider registries) { + super.loadAdditional(tag, registries); + + if(tag.contains("center")) { + this.fieldCenter = NbtUtils.readBlockPos(tag, "center").orElse(null); + } + if(tag.hasUUID("proxyId")) { + this.proxyId = tag.getUUID("proxyId"); + } + } + + public UUID getProxyId() { + return proxyId; + } +} \ No newline at end of file diff --git a/neoforge-main/src/main/java/dev/compactmods/crafting/proxies/data/MatchFieldProxyEntity.java b/neoforge-main/src/main/java/dev/compactmods/crafting/proxies/data/MatchFieldProxyEntity.java new file mode 100644 index 00000000..816fa64d --- /dev/null +++ b/neoforge-main/src/main/java/dev/compactmods/crafting/proxies/data/MatchFieldProxyEntity.java @@ -0,0 +1,37 @@ +package dev.compactmods.crafting.proxies.data; + +import dev.compactmods.crafting.api.field.IMiniaturizationField; +import dev.compactmods.crafting.core.CCBlocks; +import dev.compactmods.crafting.field.MiniaturizationField; +import dev.compactmods.crafting.proxies.listener.MatchModeProxyFieldListener; +import dev.compactmods.crafting.recipes.MiniaturizationRecipe; +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.block.state.BlockState; + +public class MatchFieldProxyEntity extends BaseFieldProxyEntity { + + protected MatchModeProxyFieldListener listener; + + public MatchFieldProxyEntity(BlockPos pos, BlockState state) { + super(CCBlocks.MATCH_PROXY_ENTITY.get(), pos, state); + } + + @Override + protected void fieldChanged(IMiniaturizationField f) { + super.fieldChanged(f); + + this.listener = new MatchModeProxyFieldListener(level, worldPosition); + + if (f instanceof MiniaturizationField mf) { + mf.registerListener(this.listener); + } + } + + @Override + public void setRemoved() { + if (field instanceof MiniaturizationField mf && listener != null) { + mf.unregisterListener(this.listener); + } + super.setRemoved(); + } +} \ No newline at end of file diff --git a/neoforge-main/gtfo/proxies/data/RescanFieldProxyEntity.java b/neoforge-main/src/main/java/dev/compactmods/crafting/proxies/data/RescanFieldProxyEntity.java similarity index 99% rename from neoforge-main/gtfo/proxies/data/RescanFieldProxyEntity.java rename to neoforge-main/src/main/java/dev/compactmods/crafting/proxies/data/RescanFieldProxyEntity.java index 7c6e96ad..e4bcc065 100644 --- a/neoforge-main/gtfo/proxies/data/RescanFieldProxyEntity.java +++ b/neoforge-main/src/main/java/dev/compactmods/crafting/proxies/data/RescanFieldProxyEntity.java @@ -8,4 +8,4 @@ public class RescanFieldProxyEntity extends BaseFieldProxyEntity { public RescanFieldProxyEntity(BlockPos pos, BlockState state) { super(CCBlocks.RESCAN_PROXY_ENTITY.get(), pos, state); } -} +} \ No newline at end of file diff --git a/neoforge-main/gtfo/proxies/item/FieldProxyItem.java b/neoforge-main/src/main/java/dev/compactmods/crafting/proxies/item/FieldProxyItem.java similarity index 60% rename from neoforge-main/gtfo/proxies/item/FieldProxyItem.java rename to neoforge-main/src/main/java/dev/compactmods/crafting/proxies/item/FieldProxyItem.java index 5a765e56..10fc79f6 100644 --- a/neoforge-main/gtfo/proxies/item/FieldProxyItem.java +++ b/neoforge-main/src/main/java/dev/compactmods/crafting/proxies/item/FieldProxyItem.java @@ -1,19 +1,19 @@ package dev.compactmods.crafting.proxies.item; import java.util.List; -import dev.compactmods.crafting.core.CCCapabilities; +import dev.compactmods.crafting.core.CCDataComponents; +import dev.compactmods.crafting.data.CCAttachments; import dev.compactmods.crafting.projector.FieldProjectorBlock; import dev.compactmods.crafting.projector.FieldProjectorEntity; import net.minecraft.ChatFormatting; import net.minecraft.core.BlockPos; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.NbtUtils; import net.minecraft.network.chat.Component; import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionResult; import net.minecraft.world.InteractionResultHolder; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.BlockItem; +import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.TooltipFlag; import net.minecraft.world.item.context.UseOnContext; @@ -28,18 +28,15 @@ public FieldProxyItem(Block block, Properties props) { } @Override - public void appendHoverText(ItemStack stack, @Nullable Level level, List text, TooltipFlag flags) { - - boolean isLinked = false; - if(stack.hasTag()) { - CompoundTag field = stack.getOrCreateTagElement("field"); - if(field.contains("center")) { - isLinked = true; - - BlockPos linkedCenter = NbtUtils.readBlockPos(field.getCompound("center")); - text.add(Component.translatable("tooltip.compactcrafting.proxy_bound", linkedCenter) - .withStyle(ChatFormatting.ITALIC).withStyle(ChatFormatting.AQUA)); - } + public void appendHoverText(ItemStack stack, Item.@Nullable TooltipContext context, List text, TooltipFlag flags) { + + var fieldCenter = stack.get(CCDataComponents.FIELD_CENTER.get()); + boolean isLinked = fieldCenter != null; + + if(isLinked) { + BlockPos linkedCenter = fieldCenter.center(); + text.add(Component.translatable("tooltip.compactcrafting.proxy_bound", linkedCenter.toString()) + .withStyle(ChatFormatting.ITALIC).withStyle(ChatFormatting.AQUA)); } if(!isLinked) { @@ -51,19 +48,18 @@ public void appendHoverText(ItemStack stack, @Nullable Level level, List use(Level level, Player player, InteractionHand hand) { ItemStack stack = player.getItemInHand(hand); if(player.isDiscrete() && hand == InteractionHand.MAIN_HAND) { - // used in the air while sneaking - player.displayClientMessage(Component.literal("clearing field data"), true); + player.displayClientMessage(Component.translatable("compactcrafting.unbinding_proxy"), true); - // clear field position - stack.removeTagKey("field"); + stack.remove(CCDataComponents.FIELD_CENTER.get()); return InteractionResultHolder.success(stack); } @@ -83,20 +79,23 @@ public InteractionResult useOn(UseOnContext context) { BlockPos usedAt = context.getClickedPos(); BlockState usedState = level.getBlockState(usedAt); - // if used on a projector while sneaking if (usedState.getBlock() instanceof FieldProjectorBlock) { - player.displayClientMessage(Component.literal("copying field position"), true); + + player.displayClientMessage(Component.translatable("compactcrafting.binding_proxy", usedAt.toString()), true); FieldProjectorEntity tile = (FieldProjectorEntity) level.getBlockEntity(usedAt); if (tile != null) { - tile.getCapability(CCCapabilities.MINIATURIZATION_FIELD) - .ifPresent(field -> { - BlockPos fieldCenter = field.getCenter(); - - // write field center - stack.getOrCreateTagElement("field") - .put("center", NbtUtils.writeBlockPos(fieldCenter)); - }); + var fields = level.getData(CCAttachments.ACTIVE_FIELDS); + + // Search through all fields to find one whose projectors include this position + fields.getFields() + .filter(field -> field.getProjectors().locations().contains(usedAt)) + .findFirst() + .ifPresent(field -> { + BlockPos fieldCenter = field.getCenter(); + + stack.set(CCDataComponents.FIELD_CENTER.get(), new CCDataComponents.FieldCenter(fieldCenter)); + }); } return InteractionResult.sidedSuccess(level.isClientSide); @@ -105,4 +104,4 @@ public InteractionResult useOn(UseOnContext context) { return super.useOn(context); } -} +} \ No newline at end of file diff --git a/neoforge-main/gtfo/proxies/listener/MatchModeProxyFieldListener.java b/neoforge-main/src/main/java/dev/compactmods/crafting/proxies/listener/MatchModeProxyFieldListener.java similarity index 66% rename from neoforge-main/gtfo/proxies/listener/MatchModeProxyFieldListener.java rename to neoforge-main/src/main/java/dev/compactmods/crafting/proxies/listener/MatchModeProxyFieldListener.java index f2edcf15..f736af84 100644 --- a/neoforge-main/gtfo/proxies/listener/MatchModeProxyFieldListener.java +++ b/neoforge-main/src/main/java/dev/compactmods/crafting/proxies/listener/MatchModeProxyFieldListener.java @@ -22,11 +22,33 @@ public MatchModeProxyFieldListener(Level level, BlockPos location) { @Override public void onRecipeChanged(IMiniaturizationField field, @Nullable IMiniaturizationRecipe recipe) { - if (level != null) { + updateProxySignal(recipe != null); + } + + @Override + public void onRecipeMatched(IMiniaturizationField field, IMiniaturizationRecipe recipe) { + updateProxySignal(true); + } + + @Override + public void onRecipeCleared(IMiniaturizationField field) { + updateProxySignal(false); + } + + @Override + public void onRecipeCompleted(IMiniaturizationField field, IMiniaturizationRecipe recipe) { + updateProxySignal(false); + } + + @Override + public void onFieldActivated(IMiniaturizationField field) { + updateProxySignal(field.currentRecipe() != null); + } + + private void updateProxySignal(boolean hasRecipe) { + if (level != null && !level.isClientSide) { BlockState currentState = level.getBlockState(location); if (currentState.getBlock() instanceof FieldProxyBlock) { - boolean hasRecipe = recipe != null; - int newPower = hasRecipe ? 15 : 0; if (currentState.getValue(FieldProxyBlock.SIGNAL) != newPower) { @@ -36,4 +58,4 @@ public void onRecipeChanged(IMiniaturizationField field, @Nullable IMiniaturizat } } } -} +} \ No newline at end of file diff --git a/neoforge-main/gtfo/proxies/render/FieldProxyColors.java b/neoforge-main/src/main/java/dev/compactmods/crafting/proxies/render/FieldProxyColors.java similarity index 89% rename from neoforge-main/gtfo/proxies/render/FieldProxyColors.java rename to neoforge-main/src/main/java/dev/compactmods/crafting/proxies/render/FieldProxyColors.java index 7090a460..e8093812 100644 --- a/neoforge-main/gtfo/proxies/render/FieldProxyColors.java +++ b/neoforge-main/src/main/java/dev/compactmods/crafting/proxies/render/FieldProxyColors.java @@ -12,6 +12,7 @@ public class FieldProxyColors { private static final int MATCH = 0xFF319a3b; private static final int RESCAN = 0xFFf062de; + public static class MatchBlock implements BlockColor { @Override public int getColor(BlockState state, @Nullable BlockAndTintGetter level, @Nullable BlockPos pos, int tintIndex) { @@ -21,7 +22,7 @@ public int getColor(BlockState state, @Nullable BlockAndTintGetter level, @Nulla public static class MatchItem implements ItemColor { @Override - public int getColor(ItemStack p_getColor_1_, int p_getColor_2_) { + public int getColor(ItemStack stack, int tintIndex) { return MATCH; } } @@ -35,8 +36,8 @@ public int getColor(BlockState state, @Nullable BlockAndTintGetter level, @Nulla public static class RescanItem implements ItemColor { @Override - public int getColor(ItemStack p_getColor_1_, int p_getColor_2_) { + public int getColor(ItemStack stack, int tintIndex) { return RESCAN; } } -} +} \ No newline at end of file diff --git a/neoforge-main/gtfo/proxies/render/ProxyRenderSetup.java b/neoforge-main/src/main/java/dev/compactmods/crafting/proxies/render/ProxyRenderSetup.java similarity index 52% rename from neoforge-main/gtfo/proxies/render/ProxyRenderSetup.java rename to neoforge-main/src/main/java/dev/compactmods/crafting/proxies/render/ProxyRenderSetup.java index 079d8c7c..e9ee5943 100644 --- a/neoforge-main/gtfo/proxies/render/ProxyRenderSetup.java +++ b/neoforge-main/src/main/java/dev/compactmods/crafting/proxies/render/ProxyRenderSetup.java @@ -5,14 +5,14 @@ import dev.compactmods.crafting.core.CCItems; import net.minecraft.client.renderer.ItemBlockRenderTypes; import net.minecraft.client.renderer.RenderType; -import net.minecraftforge.api.distmarker.Dist; -import net.minecraftforge.client.event.RegisterColorHandlersEvent; -import net.minecraftforge.eventbus.api.SubscribeEvent; -import net.minecraftforge.fml.common.Mod; -import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent; +import net.neoforged.api.distmarker.Dist; +import net.neoforged.bus.api.SubscribeEvent; +import net.neoforged.fml.common.EventBusSubscriber; +import net.neoforged.fml.event.lifecycle.FMLClientSetupEvent; +import net.neoforged.neoforge.client.event.RegisterColorHandlersEvent; @SuppressWarnings("unused") -@Mod.EventBusSubscriber(modid = CompactCrafting.MOD_ID, value = Dist.CLIENT, bus = Mod.EventBusSubscriber.Bus.MOD) +@EventBusSubscriber(modid = CompactCrafting.MOD_ID, value = Dist.CLIENT, bus = EventBusSubscriber.Bus.MOD) public class ProxyRenderSetup { @SubscribeEvent @@ -24,13 +24,13 @@ public static void init(final FMLClientSetupEvent event) { @SubscribeEvent public static void onBlockColors(final RegisterColorHandlersEvent.Block colors) { // color the ring at the base of the proxy poles - colors.getBlockColors().register(new FieldProxyColors.MatchBlock(), CCBlocks.MATCH_FIELD_PROXY_BLOCK.get()); - colors.getBlockColors().register(new FieldProxyColors.RescanBlock(), CCBlocks.RESCAN_FIELD_PROXY_BLOCK.get()); + colors.register(new FieldProxyColors.MatchBlock(), CCBlocks.MATCH_FIELD_PROXY_BLOCK.get()); + colors.register(new FieldProxyColors.RescanBlock(), CCBlocks.RESCAN_FIELD_PROXY_BLOCK.get()); } @SubscribeEvent public static void onItemColors(final RegisterColorHandlersEvent.Item itemColors) { - itemColors.getItemColors().register(new FieldProxyColors.MatchItem(), CCItems.MATCH_PROXY_ITEM.get()); - itemColors.getItemColors().register(new FieldProxyColors.RescanItem(), CCItems.RESCAN_PROXY_ITEM.get()); + itemColors.register(new FieldProxyColors.MatchItem(), CCItems.MATCH_PROXY_ITEM.get()); + itemColors.register(new FieldProxyColors.RescanItem(), CCItems.RESCAN_PROXY_ITEM.get()); } -} +} \ No newline at end of file diff --git a/neoforge-main/src/main/java/dev/compactmods/crafting/recipes/MiniaturizationRecipe.java b/neoforge-main/src/main/java/dev/compactmods/crafting/recipes/MiniaturizationRecipe.java index 72ab712f..c6b51e59 100644 --- a/neoforge-main/src/main/java/dev/compactmods/crafting/recipes/MiniaturizationRecipe.java +++ b/neoforge-main/src/main/java/dev/compactmods/crafting/recipes/MiniaturizationRecipe.java @@ -54,6 +54,7 @@ public record MiniaturizationRecipe( TreeMap layers, ItemPredicate catalystMatcher, + ItemStack catalyst, ItemStack[] outputs, AABB dimensions, int requiredTime, @@ -70,6 +71,7 @@ public record MiniaturizationRecipe( public static final Codec COMPONENT_CODEC = RecipeComponentTypeCodec.INSTANCE.dispatchStable(IRecipeComponent::getType, RecipeComponentType::getCodec); + public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(i -> i.group( Codec.INT.optionalFieldOf("craftingTime", 200) .forGetter(MiniaturizationRecipe::getCraftingTime), @@ -86,8 +88,8 @@ public record MiniaturizationRecipe( ItemStack.STRICT_CODEC.listOf().fieldOf("outputs") .forGetter(MiniaturizationRecipe::codecOutputs), - ItemPredicate.CODEC.fieldOf("catalyst") - .forGetter(MiniaturizationRecipe::catalystTest) + ItemStack.STRICT_CODEC.fieldOf("catalyst") + .forGetter(MiniaturizationRecipe::catalyst) ).apply(i, MiniaturizationRecipe::fromCodec)); @@ -97,7 +99,7 @@ public record MiniaturizationRecipe( ByteBufCodecs.fromCodec(LAYER_CODEC).apply(ByteBufCodecs.list()), MiniaturizationRecipe::codecLayerList, MiniaturizationRecipeComponents.STREAM_CODEC, MiniaturizationRecipe::components, ItemStack.LIST_STREAM_CODEC, MiniaturizationRecipe::codecOutputs, - ByteBufCodecs.fromCodecWithRegistries(ItemPredicate.CODEC), MiniaturizationRecipe::catalystMatcher, + ItemStack.STREAM_CODEC, MiniaturizationRecipe::catalyst, MiniaturizationRecipe::fromCodec ); @@ -107,9 +109,11 @@ public record MiniaturizationRecipe( RecipeHolder::new ); + + public static MiniaturizationRecipe fromCodec(int craftTime, int recipeSize, List layers, MiniaturizationRecipeComponents components, List outputs, - ItemPredicate catalyst) { + ItemStack catalyst) { var layers1 = new TreeMap(); // region Layers @@ -167,24 +171,28 @@ public static MiniaturizationRecipe fromCodec(int craftTime, int recipeSize, Lis recipeDims = new AABB(Vec3.ZERO, new Vec3(x, height, z)); } + updateFluidLayerDimensions(layers1, recipeDims); + HashMap componentTotals = new HashMap<>(); components.getAllComponents().keySet().forEach(comp -> { int count = getComponentRequiredCount(comp, components, layers1); componentTotals.put(comp, count); }); - var recipe = new MiniaturizationRecipe(layers1, catalyst, outputs.toArray(new ItemStack[0]), + ItemPredicate catalystPredicate = ItemPredicate.Builder.item() + .of(catalyst.getItem()) + .build(); + + var recipe = new MiniaturizationRecipe(layers1, catalystPredicate, catalyst, outputs.toArray(new ItemStack[0]), recipeDims, craftTime, hasFixedFootprint, componentTotals, components); - recipe.updateFluidLayerDimensions(); return recipe; } - private void updateFluidLayerDimensions() { - // Update all the dynamic recipe layers + private static void updateFluidLayerDimensions(TreeMap layers, AABB dimensions) { final AABB footprint = BlockSpaceUtil.getLayerBounds(dimensions, 0); - this.layers.values() + layers.values() .stream() .filter(l -> l instanceof IDynamicSizedRecipeLayer) .forEach(dl -> ((IDynamicSizedRecipeLayer) dl).setRecipeDimensions(footprint)); diff --git a/neoforge-main/src/main/resources/assets/compactcrafting/lang/en_us.json b/neoforge-main/src/main/resources/assets/compactcrafting/lang/en_us.json index 664504ef..e43b0b88 100644 --- a/neoforge-main/src/main/resources/assets/compactcrafting/lang/en_us.json +++ b/neoforge-main/src/main/resources/assets/compactcrafting/lang/en_us.json @@ -5,6 +5,9 @@ "block.compactcrafting.rescan_proxy": "Field Proxy (Rescan)", "block.compactcrafting.match_proxy": "Field Proxy (Match)", + "compactcrafting.binding_proxy": "Binding Proxy to field at %s", + "compactcrafting.unbinding_proxy": "Unbinding Proxy from field", + "item.compactcrafting.projector_dish": "Miniaturization Projector Dish", "item.compactcrafting.base": "Base", @@ -15,11 +18,15 @@ "compactcrafting.top.current_recipe": "Recipe: ", "compactcrafting.top.awaiting_catalyst": "Awaiting catalyst...", "compactcrafting.top.progress": "%s / %s", + "compactcrafting.top.proxy_no_recipe": "No recipe", + "compactcrafting.top.proxy_has_recipe": "Found valid recipe", + "compactcrafting.top.proxy_bound": "Bound to: %s", - "tooltip.compactcrafting.proxy_bound": "Bound to: %s", + "tooltip.compactcrafting.proxy_bound": "Bound to field at %s", "tooltip.compactcrafting.proxy_hint": "Use proxies for field automation with redstone.", "tooltip.compactcrafting.proxy_bind_hint": "Shift-use on a projector to bind to a field.", "tooltip.compactcrafting.proxy_unbind_hint": "Shift-use in the air to unbind from field.", + "tooltip.compactcrafting.proxy_highlight_hint": "Right-click on placed proxy to highlight projectors.", "messages.compactcrafting.no_field_cap": "The block at %s does not have a field reference.", "messages.compactcrafting.no_field_found": "The miniaturization field info could not be found.", diff --git a/neoforge-main/src/main/resources/data/compactcrafting/recipe/compact_walls.json b/neoforge-main/src/main/resources/data/compactcrafting/recipe/compact_walls.json index eda73d75..acb464e3 100644 --- a/neoforge-main/src/main/resources/data/compactcrafting/recipe/compact_walls.json +++ b/neoforge-main/src/main/resources/data/compactcrafting/recipe/compact_walls.json @@ -26,7 +26,7 @@ } }, "outputs": [{ - "id": "minecraft:diamond", - "Count": 1 + "id": "compactmachines:wall", + "Count": 16 }] } \ No newline at end of file diff --git a/neoforge-main/src/main/resources/data/compactcrafting/recipe/ender_crystal.json b/neoforge-main/src/main/resources/data/compactcrafting/recipe/ender_crystal.json index 906fae75..fd6ebfb7 100644 --- a/neoforge-main/src/main/resources/data/compactcrafting/recipe/ender_crystal.json +++ b/neoforge-main/src/main/resources/data/compactcrafting/recipe/ender_crystal.json @@ -32,7 +32,7 @@ ], "catalyst": { - "items": ["minecraft:ender_pearl"] + "id": "minecraft:ender_pearl" }, "components": { diff --git a/neoforge-main/src/test/java/dev/compactmods/crafting/test/junit/recipes/data/MiniaturizationRecipeCodecTests.java b/neoforge-main/src/test/java/dev/compactmods/crafting/test/junit/recipes/data/MiniaturizationRecipeCodecTests.java index 540a88d5..776fdfbe 100644 --- a/neoforge-main/src/test/java/dev/compactmods/crafting/test/junit/recipes/data/MiniaturizationRecipeCodecTests.java +++ b/neoforge-main/src/test/java/dev/compactmods/crafting/test/junit/recipes/data/MiniaturizationRecipeCodecTests.java @@ -13,7 +13,6 @@ import dev.compactmods.crafting.test.FileHelper; import net.minecraft.nbt.NbtOps; import net.minecraft.nbt.Tag; -import net.minecraft.resources.RegistryOps; import net.minecraft.server.MinecraftServer; import net.neoforged.testframework.junit.EphemeralTestServerProvider; import org.junit.jupiter.api.Assertions; @@ -37,11 +36,11 @@ public void LoadsRecipeFromJson(final MinecraftServer server) { public void DoesNotFailIfNoComponentsDefined(final MinecraftServer server) { JsonElement json = FileHelper.getJsonFromFile("recipe_tests/warn_no_components.json"); - final var ops = RegistryOps.create(JsonOps.INSTANCE, server.registryAccess()); final var result = MiniaturizationRecipe.CODEC .codec() - .parse(ops, json) - .getOrThrow(); + .parse(JsonOps.INSTANCE, json) + .resultOrPartial(Assertions::fail) + .orElseThrow(); final IRecipeComponents components = result.getComponents(); if (components == null) {