diff --git a/README.md b/README.md index 1ce5e6ee..4fc4c909 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ https://www.curseforge.com/minecraft/mc-mods/antarchy ANTS! ANTS! ANTS! -Antarchy requires Architectury API, AntOS, Integrated API, GeckoLib, and Sizeable Folia. +Antarchy requires Architectury API, AntOS, Integrated API, GeckoLib, and Sizeable Foliage. ## Credits diff --git a/common-1.20.1/src/main/java/com/craisinlord/antarchy/config/AntarchyConfigCatalog.java b/common-1.20.1/src/main/java/com/craisinlord/antarchy/config/AntarchyConfigCatalog.java index 1efd5ec0..cda374f1 100644 --- a/common-1.20.1/src/main/java/com/craisinlord/antarchy/config/AntarchyConfigCatalog.java +++ b/common-1.20.1/src/main/java/com/craisinlord/antarchy/config/AntarchyConfigCatalog.java @@ -110,7 +110,7 @@ public final class AntarchyConfigCatalog { || name.startsWith("nightmareSword") || name.startsWith("basiliskDagger") || name.startsWith("squidzooka") || name.equals("invertProjectilesFromInvertedPlayers") || name.startsWith("gravityGun") || name.startsWith("critterCage") - || name.startsWith("minersDream") || name.equals("potentNyxiteInvertedDurationSeconds") + || name.equals("potentNyxiteInvertedDurationSeconds") || name.equals("corneaEarNightVisionSeconds") || name.startsWith("american") || name.startsWith("mogglesVision") || name.equals("ductTapeRepairPercentPerUse") || name.startsWith("fallenKingCrown"); diff --git a/common-1.20.1/src/main/java/com/craisinlord/antarchy/config/AntarchySettings.java b/common-1.20.1/src/main/java/com/craisinlord/antarchy/config/AntarchySettings.java index d4449786..df4311b1 100644 --- a/common-1.20.1/src/main/java/com/craisinlord/antarchy/config/AntarchySettings.java +++ b/common-1.20.1/src/main/java/com/craisinlord/antarchy/config/AntarchySettings.java @@ -163,11 +163,6 @@ public final class AntarchySettings { private static double gravityGunMaxHoldDistance = 10.0D; private static double critterCageMaxCapturableWidth = 1.65D; private static double critterCageMaxCapturableHeight = 1.65D; - private static boolean minersDreamEnabled = true; - private static int minersDreamMinimumRange = 48; - private static int minersDreamMaximumRange = 96; - private static int minersDreamTorchSpacing = 10; - private static int minersDreamBlocksPerTick = 150; private static double lucidAttackRange = 9.0D; private static double lucidPearlInvertedDurationSeconds = 6.0D; private static int corneaEarNightVisionSeconds = 15; @@ -1189,26 +1184,6 @@ public final class AntarchySettings { return critterCageMaxCapturableHeight; } - public static boolean minersDreamEnabled() { - return minersDreamEnabled; - } - - public static int minersDreamMinimumRange() { - return minersDreamMinimumRange; - } - - public static int minersDreamMaximumRange() { - return minersDreamMaximumRange; - } - - public static int minersDreamTorchSpacing() { - return minersDreamTorchSpacing; - } - - public static int minersDreamBlocksPerTick() { - return minersDreamBlocksPerTick; - } - public static void setDisableInfinityBookPortalCreation(boolean value) { disableInfinityBookPortalCreation = value; } @@ -1731,26 +1706,6 @@ public final class AntarchySettings { critterCageMaxCapturableHeight = value; } - public static void setMinersDreamEnabled(boolean value) { - minersDreamEnabled = value; - } - - public static void setMinersDreamMinimumRange(int value) { - minersDreamMinimumRange = value; - } - - public static void setMinersDreamMaximumRange(int value) { - minersDreamMaximumRange = value; - } - - public static void setMinersDreamTorchSpacing(int value) { - minersDreamTorchSpacing = value; - } - - public static void setMinersDreamBlocksPerTick(int value) { - minersDreamBlocksPerTick = value; - } - public static double lucidAttackRange() { return lucidAttackRange; } diff --git a/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/AntarchyTags.java b/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/AntarchyTags.java index fb50d407..07474827 100644 --- a/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/AntarchyTags.java +++ b/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/AntarchyTags.java @@ -40,7 +40,6 @@ public final class AntarchyTags { public static final TagKey CAVARYN_PORTAL_FRAMES = TagKey.create(Registries.BLOCK, id("cavaryn_portal_frames")); public static final TagKey RAINBOW_PORTAL_FRAMES = TagKey.create(Registries.BLOCK, id("rainbow_portal_frames")); public static final TagKey ROLLY_CAVERN_CARVEABLE = TagKey.create(Registries.BLOCK, id("rolly_cavern_carveable")); - public static final TagKey MINERS_DREAM_CARVABLE = TagKey.create(Registries.BLOCK, id("miners_dream_carvable")); public static final TagKey BLUESTONE_COMPONENTS = TagKey.create(Registries.BLOCK, id("bluestone_components")); public static final TagKey BLUESTONE_CONNECTABLE = TagKey.create(Registries.BLOCK, id("bluestone_connectable")); public static final TagKey ANTIMETAL_INVERSION_BLOCKS = TagKey.create(Registries.BLOCK, id("antimetal_inversion_blocks")); diff --git a/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/block/entity/UpperBlockEntity.java b/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/block/entity/UpperBlockEntity.java index 1e5f62e0..6fb68c2f 100644 --- a/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/block/entity/UpperBlockEntity.java +++ b/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/block/entity/UpperBlockEntity.java @@ -19,6 +19,7 @@ import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.inventory.AbstractContainerMenu; import net.minecraft.world.inventory.HopperMenu; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.ChestBlock; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.entity.RandomizableContainerBlockEntity; @@ -118,6 +119,10 @@ public class UpperBlockEntity extends RandomizableContainerBlockEntity { } private static Container getContainerAt(ServerLevel level, BlockPos pos) { + BlockState state = level.getBlockState(pos); + if (state.getBlock() instanceof ChestBlock chestBlock) { + return ChestBlock.getContainer(chestBlock, state, level, pos, false); + } BlockEntity blockEntity = level.getBlockEntity(pos); return blockEntity instanceof Container container ? container : null; } diff --git a/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/item/MinersDreamCaveGenerator.java b/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/item/MinersDreamCaveGenerator.java deleted file mode 100644 index 4aa42bac..00000000 --- a/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/item/MinersDreamCaveGenerator.java +++ /dev/null @@ -1,173 +0,0 @@ -package com.craisinlord.antarchy.content.item; - -import com.craisinlord.antarchy.content.AntarchyTags; -import it.unimi.dsi.fastutil.longs.LongOpenHashSet; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.util.Mth; -import net.minecraft.util.RandomSource; -import net.minecraft.world.level.block.state.BlockState; - -public final class MinersDreamCaveGenerator { - private static final double MIN_HORIZONTAL_RADIUS = 2.0D; - private static final double MAX_HORIZONTAL_RADIUS = 5.0D; - private static final double MIN_VERTICAL_RADIUS = 1.5D; - private static final double MAX_VERTICAL_RADIUS = 4.0D; - private static final double MAX_VERTICAL_DEVIATION = 8.0D; - private static final double SOFT_VERTICAL_DEVIATION = 4.0D; - private static final int WORLD_MARGIN = 3; - - private MinersDreamCaveGenerator() { - } - - public record CaveNode(BlockPos center, double horizontalRadius, double verticalRadius) { - } - - public record CaveGenerationResult(List nodes, Set torchCheckpoints) { - } - - public static CaveGenerationResult generate(BlockPos start, Direction initialDirection, int length, - int torchSpacing, int minBuildHeight, int maxBuildHeight, RandomSource random) { - List nodes = new ArrayList<>(length); - Set torchCheckpoints = new HashSet<>(); - - double x = start.getX() + 0.5D; - double y = start.getY() + 0.5D; - double z = start.getZ() + 0.5D; - double startY = y; - - double headingX = initialDirection.getStepX(); - double headingZ = initialDirection.getStepZ(); - if (headingX == 0.0D && headingZ == 0.0D) { - headingX = 1.0D; - } - - double verticalVelocity = 0.0D; - double horizontalRadius = 3.0D; - double verticalRadius = 2.0D; - int minY = minBuildHeight + WORLD_MARGIN; - int maxY = maxBuildHeight - WORLD_MARGIN; - - int spacing = Math.max(4, torchSpacing); - int traveledSinceTorch = 0; - int nextTorchDistance = spacing + random.nextInt(5) - 2; - - for (int i = 0; i < length; i++) { - BlockPos center = BlockPos.containing(x, Mth.clamp(y, minY, maxY), z); - nodes.add(new CaveNode(center, horizontalRadius, verticalRadius)); - - traveledSinceTorch++; - if (traveledSinceTorch >= Math.max(1, nextTorchDistance)) { - torchCheckpoints.add(i); - traveledSinceTorch = 0; - nextTorchDistance = spacing + random.nextInt(5) - 2; - } - - double turnDegrees = random.nextGaussian() * 8.0D; - if (random.nextFloat() < 0.1F) { - turnDegrees = random.nextGaussian() * 15.0D; - } - turnDegrees = Mth.clamp(turnDegrees, -25.0D, 25.0D); - double turnRadians = Math.toRadians(turnDegrees); - double cos = Math.cos(turnRadians); - double sin = Math.sin(turnRadians); - double newHeadingX = headingX * cos - headingZ * sin; - double newHeadingZ = headingX * sin + headingZ * cos; - double headingLength = Math.sqrt(newHeadingX * newHeadingX + newHeadingZ * newHeadingZ); - headingX = newHeadingX / headingLength; - headingZ = newHeadingZ / headingLength; - - verticalVelocity += random.nextGaussian() * 0.15D; - double deviation = y - startY; - verticalVelocity -= deviation * 0.06D; - if (deviation > SOFT_VERTICAL_DEVIATION) { - verticalVelocity -= 0.08D; - } else if (deviation < -SOFT_VERTICAL_DEVIATION) { - verticalVelocity += 0.08D; - } - verticalVelocity = Mth.clamp(verticalVelocity, -0.6D, 0.6D); - - x += headingX; - z += headingZ; - y = Mth.clamp(y + verticalVelocity, startY - MAX_VERTICAL_DEVIATION, startY + MAX_VERTICAL_DEVIATION); - - horizontalRadius = Mth.clamp(horizontalRadius + random.nextGaussian() * 0.15D, MIN_HORIZONTAL_RADIUS, MAX_HORIZONTAL_RADIUS); - verticalRadius = Mth.clamp(verticalRadius + random.nextGaussian() * 0.12D, MIN_VERTICAL_RADIUS, MAX_VERTICAL_RADIUS); - if (random.nextFloat() < 0.05F) { - horizontalRadius = Mth.clamp(horizontalRadius + 0.6D, MIN_HORIZONTAL_RADIUS, MAX_HORIZONTAL_RADIUS); - verticalRadius = Mth.clamp(verticalRadius + 0.5D, MIN_VERTICAL_RADIUS, MAX_VERTICAL_RADIUS); - } - } - - return new CaveGenerationResult(nodes, torchCheckpoints); - } - - public static void collectNodeCandidates(ServerLevel level, CaveNode node, LongOpenHashSet visited, RandomSource random, List out) { - BlockPos center = node.center(); - int hr = Mth.ceil(node.horizontalRadius()); - double upExtent = node.verticalRadius() * 1.25D; - double downExtent = node.verticalRadius() * 0.65D; - int upR = Mth.ceil(upExtent); - int downR = Mth.ceil(downExtent); - int minBuildHeight = level.getMinBuildHeight(); - int maxBuildHeight = level.getMaxBuildHeight(); - - for (int dy = -downR; dy <= upR; dy++) { - double vExtent = dy >= 0 ? upExtent : downExtent; - if (vExtent <= 0.0D) { - continue; - } - double vTerm = (dy * dy) / (vExtent * vExtent); - if (vTerm > 1.05D) { - continue; - } - - for (int dx = -hr; dx <= hr; dx++) { - double hxTerm = (dx * dx) / (node.horizontalRadius() * node.horizontalRadius()); - for (int dz = -hr; dz <= hr; dz++) { - double hzTerm = (dz * dz) / (node.horizontalRadius() * node.horizontalRadius()); - double distance = hxTerm + vTerm + hzTerm; - if (distance > 1.0D) { - continue; - } - if (distance > 0.7D) { - double edgeChance = (1.0D - distance) / 0.3D * 0.85D; - if (random.nextFloat() > edgeChance) { - continue; - } - } - - BlockPos pos = center.offset(dx, dy, dz); - if (pos.getY() < minBuildHeight || pos.getY() >= maxBuildHeight) { - continue; - } - - long packed = pos.asLong(); - if (visited.contains(packed)) { - continue; - } - - BlockState state = level.getBlockState(pos); - if (!state.is(AntarchyTags.Blocks.MINERS_DREAM_CARVABLE)) { - continue; - } - if (level.getBlockEntity(pos) != null) { - continue; - } - - visited.add(packed); - out.add(pos); - } - } - } - } - - public static boolean isChunkLoaded(ServerLevel level, BlockPos pos) { - return level.isLoaded(pos); - } -} diff --git a/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/item/MinersDreamExcavation.java b/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/item/MinersDreamExcavation.java deleted file mode 100644 index 6d7e01a0..00000000 --- a/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/item/MinersDreamExcavation.java +++ /dev/null @@ -1,152 +0,0 @@ -package com.craisinlord.antarchy.content.item; - -import it.unimi.dsi.fastutil.longs.LongOpenHashSet; -import java.util.ArrayList; -import java.util.List; -import java.util.Set; -import java.util.UUID; -import net.minecraft.core.BlockPos; -import net.minecraft.core.particles.BlockParticleOption; -import net.minecraft.core.particles.DustParticleOptions; -import net.minecraft.core.particles.ParticleTypes; -import net.minecraft.resources.ResourceKey; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.sounds.SoundEvents; -import net.minecraft.sounds.SoundSource; -import net.minecraft.util.RandomSource; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.block.Blocks; -import net.minecraft.world.level.block.state.BlockState; -import org.joml.Vector3f; - -public final class MinersDreamExcavation { - private static final Vector3f TRIFFID_GOO_COLOR = new Vector3f(0.35F, 0.85F, 0.4F); - private static final int SLICES_BETWEEN_RUMBLE = 4; - - private final UUID owner; - private final ResourceKey dimension; - private final List nodes; - private final Set torchCheckpoints; - private final LongOpenHashSet visited = new LongOpenHashSet(); - - private int nodeIndex = 0; - private List currentNodePositions = null; - private int positionCursor = 0; - private int slicesSinceRumble = 0; - private boolean finished = false; - - public MinersDreamExcavation(UUID owner, ResourceKey dimension, List nodes, Set torchCheckpoints) { - this.owner = owner; - this.dimension = dimension; - this.nodes = nodes; - this.torchCheckpoints = torchCheckpoints; - } - - public UUID owner() { - return owner; - } - - public ResourceKey dimension() { - return dimension; - } - - public boolean isFinished() { - return finished; - } - - public void tick(ServerLevel level, int blockBudget) { - RandomSource random = level.getRandom(); - int remaining = blockBudget; - - while (remaining > 0 && !finished) { - if (currentNodePositions == null) { - if (nodeIndex >= nodes.size()) { - finished = true; - playCompletionSound(level); - return; - } - - MinersDreamCaveGenerator.CaveNode node = nodes.get(nodeIndex); - if (!MinersDreamCaveGenerator.isChunkLoaded(level, node.center())) { - advanceNode(level, node); - continue; - } - - currentNodePositions = new ArrayList<>(); - MinersDreamCaveGenerator.collectNodeCandidates(level, node, visited, random, currentNodePositions); - positionCursor = 0; - } - - if (currentNodePositions.isEmpty()) { - MinersDreamCaveGenerator.CaveNode node = nodes.get(nodeIndex); - advanceNode(level, node); - continue; - } - - int endIndex = Math.min(currentNodePositions.size(), positionCursor + remaining); - for (; positionCursor < endIndex; positionCursor++) { - BlockPos pos = currentNodePositions.get(positionCursor); - BlockState removedState = level.getBlockState(pos); - if (removedState.isAir()) { - continue; - } - level.setBlock(pos, Blocks.AIR.defaultBlockState(), 3); - remaining--; - if (random.nextFloat() < 0.12F) { - spawnBreakParticles(level, pos, removedState, random); - } - } - - if (positionCursor >= currentNodePositions.size()) { - MinersDreamCaveGenerator.CaveNode node = nodes.get(nodeIndex); - advanceNode(level, node); - } - } - } - - private void advanceNode(ServerLevel level, MinersDreamCaveGenerator.CaveNode node) { - RandomSource random = level.getRandom(); - spawnFrontDust(level, node, random); - slicesSinceRumble++; - if (slicesSinceRumble >= SLICES_BETWEEN_RUMBLE) { - slicesSinceRumble = 0; - playRumble(level, node); - } - - if (torchCheckpoints.contains(nodeIndex)) { - MinersDreamTorchPlacer.tryPlaceTorch(level, node.center(), random); - } - - currentNodePositions = null; - positionCursor = 0; - nodeIndex++; - } - - private void spawnBreakParticles(ServerLevel level, BlockPos pos, BlockState state, RandomSource random) { - level.sendParticles(new BlockParticleOption(ParticleTypes.BLOCK, state), - pos.getX() + 0.5D, pos.getY() + 0.5D, pos.getZ() + 0.5D, 2, 0.25D, 0.25D, 0.25D, 0.02D); - if (random.nextFloat() < 0.2F) { - level.sendParticles(new DustParticleOptions(TRIFFID_GOO_COLOR, 1.0F), - pos.getX() + 0.5D, pos.getY() + 0.5D, pos.getZ() + 0.5D, 1, 0.2D, 0.2D, 0.2D, 0.0D); - } - } - - private void spawnFrontDust(ServerLevel level, MinersDreamCaveGenerator.CaveNode node, RandomSource random) { - BlockPos center = node.center(); - level.sendParticles(ParticleTypes.CLOUD, center.getX() + 0.5D, center.getY() + 0.5D, center.getZ() + 0.5D, - 3, node.horizontalRadius() * 0.4D, node.verticalRadius() * 0.3D, node.horizontalRadius() * 0.4D, 0.01D); - } - - private void playRumble(ServerLevel level, MinersDreamCaveGenerator.CaveNode node) { - BlockPos center = node.center(); - level.playSound(null, center, SoundEvents.STONE_BREAK, SoundSource.BLOCKS, 0.5F, 0.7F); - } - - private void playCompletionSound(ServerLevel level) { - if (nodes.isEmpty()) { - return; - } - BlockPos center = nodes.get(nodes.size() - 1).center(); - level.playSound(null, center, SoundEvents.BEACON_ACTIVATE, SoundSource.PLAYERS, 0.4F, 1.4F); - } -} diff --git a/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/item/MinersDreamExcavationManager.java b/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/item/MinersDreamExcavationManager.java deleted file mode 100644 index 4effa987..00000000 --- a/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/item/MinersDreamExcavationManager.java +++ /dev/null @@ -1,81 +0,0 @@ -package com.craisinlord.antarchy.content.item; - -import com.craisinlord.antarchy.config.AntarchySettings; -import java.util.Iterator; -import java.util.Map; -import java.util.UUID; -import java.util.concurrent.ConcurrentHashMap; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.util.Mth; -import net.minecraft.util.RandomSource; - -public final class MinersDreamExcavationManager { - private static final int MAX_ACTIVE_TASKS = 8; - private static final int SAFE_MIN_RANGE = 8; - private static final int SAFE_MAX_RANGE = 256; - private static final Map ACTIVE = new ConcurrentHashMap<>(); - - private MinersDreamExcavationManager() { - } - - public static boolean hasActiveExcavation(UUID owner) { - return ACTIVE.containsKey(owner); - } - - public static MinersDreamExcavation tryStart(ServerLevel level, UUID owner, BlockPos startPos, Direction direction) { - if (ACTIVE.containsKey(owner) || ACTIVE.size() >= MAX_ACTIVE_TASKS) { - return null; - } - - int minRange = Mth.clamp(AntarchySettings.minersDreamMinimumRange(), SAFE_MIN_RANGE, SAFE_MAX_RANGE); - int maxRange = Mth.clamp(AntarchySettings.minersDreamMaximumRange(), SAFE_MIN_RANGE, SAFE_MAX_RANGE); - if (minRange > maxRange) { - int swap = minRange; - minRange = maxRange; - maxRange = swap; - } - - RandomSource random = level.getRandom(); - int length = minRange == maxRange ? minRange : minRange + random.nextInt(maxRange - minRange + 1); - - MinersDreamCaveGenerator.CaveGenerationResult result = MinersDreamCaveGenerator.generate( - startPos, - direction, - length, - AntarchySettings.minersDreamTorchSpacing(), - level.getMinBuildHeight(), - level.getMaxBuildHeight(), - random - ); - - if (result.nodes().isEmpty()) { - return null; - } - - MinersDreamExcavation excavation = new MinersDreamExcavation(owner, level.dimension(), result.nodes(), result.torchCheckpoints()); - ACTIVE.put(owner, excavation); - return excavation; - } - - public static void tick(ServerLevel level) { - if (ACTIVE.isEmpty()) { - return; - } - - int budget = Math.max(1, AntarchySettings.minersDreamBlocksPerTick()); - Iterator> iterator = ACTIVE.entrySet().iterator(); - while (iterator.hasNext()) { - MinersDreamExcavation excavation = iterator.next().getValue(); - if (!excavation.dimension().equals(level.dimension())) { - continue; - } - - excavation.tick(level, budget); - if (excavation.isFinished()) { - iterator.remove(); - } - } - } -} diff --git a/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/item/MinersDreamItem.java b/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/item/MinersDreamItem.java deleted file mode 100644 index d736aeea..00000000 --- a/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/item/MinersDreamItem.java +++ /dev/null @@ -1,79 +0,0 @@ -package com.craisinlord.antarchy.content.item; - -import com.craisinlord.antarchy.config.AntarchySettings; -import java.util.List; -import net.minecraft.ChatFormatting; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.network.chat.Component; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.sounds.SoundEvents; -import net.minecraft.sounds.SoundSource; -import net.minecraft.world.InteractionResult; -import net.minecraft.world.entity.player.Player; -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; -import net.minecraft.world.level.Level; - -public final class MinersDreamItem extends Item { - public MinersDreamItem(Properties properties) { - super(properties); - } - - @Override - public InteractionResult useOn(UseOnContext context) { - Level level = context.getLevel(); - Player player = context.getPlayer(); - if (player == null) { - return InteractionResult.PASS; - } - - if (!AntarchySettings.minersDreamEnabled()) { - if (!level.isClientSide()) { - player.displayClientMessage(Component.translatable("message.antarchy.miners_dream_disabled"), true); - } - return InteractionResult.FAIL; - } - - if (level.isClientSide()) { - return InteractionResult.SUCCESS; - } - - ServerLevel serverLevel = (ServerLevel) level; - BlockPos clickedPos = context.getClickedPos(); - Direction clickedFace = context.getClickedFace(); - Direction travelDirection = clickedFace.getAxis().isHorizontal() ? clickedFace : player.getDirection(); - BlockPos startPos = clickedPos.relative(travelDirection); - - if (!serverLevel.isInWorldBounds(startPos) || !MinersDreamCaveGenerator.isChunkLoaded(serverLevel, startPos)) { - return InteractionResult.FAIL; - } - - if (MinersDreamExcavationManager.hasActiveExcavation(player.getUUID())) { - player.displayClientMessage(Component.translatable("message.antarchy.miners_dream_busy"), true); - return InteractionResult.FAIL; - } - - MinersDreamExcavation excavation = MinersDreamExcavationManager.tryStart(serverLevel, player.getUUID(), startPos, travelDirection); - if (excavation == null) { - return InteractionResult.FAIL; - } - - serverLevel.playSound(null, clickedPos, SoundEvents.TNT_PRIMED, SoundSource.PLAYERS, 0.8F, 1.1F); - - if (!player.getAbilities().instabuild) { - context.getItemInHand().shrink(1); - } - - return InteractionResult.CONSUME; - } - - @Override - public void appendHoverText(ItemStack stack, Level level, List tooltipComponents, TooltipFlag tooltipFlag) { - tooltipComponents.add(Component.translatable("tooltip.antarchy.miners_dream.line1").withStyle(ChatFormatting.GRAY)); - tooltipComponents.add(Component.translatable("tooltip.antarchy.miners_dream.line2").withStyle(ChatFormatting.GRAY)); - super.appendHoverText(stack, level, tooltipComponents, tooltipFlag); - } -} diff --git a/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/item/MinersDreamTorchPlacer.java b/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/item/MinersDreamTorchPlacer.java deleted file mode 100644 index 08e8f78b..00000000 --- a/common-1.20.1/src/main/java/com/craisinlord/antarchy/content/item/MinersDreamTorchPlacer.java +++ /dev/null @@ -1,89 +0,0 @@ -package com.craisinlord.antarchy.content.item; - -import java.util.List; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.sounds.SoundSource; -import net.minecraft.util.RandomSource; -import net.minecraft.world.level.block.Blocks; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.block.state.properties.BlockStateProperties; - -public final class MinersDreamTorchPlacer { - private MinersDreamTorchPlacer() { - } - - public static void tryPlaceTorch(ServerLevel level, BlockPos center, RandomSource random) { - BlockPos floorSpot = findFloorSpot(level, center); - boolean floorFirst = random.nextBoolean(); - - if (floorFirst) { - if (floorSpot != null && placeFloorTorch(level, floorSpot)) { - return; - } - placeWallTorch(level, center, random); - return; - } - - if (placeWallTorch(level, center, random)) { - return; - } - if (floorSpot != null) { - placeFloorTorch(level, floorSpot); - } - } - - private static BlockPos findFloorSpot(ServerLevel level, BlockPos center) { - for (int dy = -2; dy <= 1; dy++) { - BlockPos candidate = center.offset(0, dy, 0); - if (!isOpenForTorch(level, candidate)) { - continue; - } - BlockState below = level.getBlockState(candidate.below()); - if (below.isFaceSturdy(level, candidate.below(), Direction.UP)) { - return candidate; - } - } - return null; - } - - private static boolean placeFloorTorch(ServerLevel level, BlockPos pos) { - if (!isOpenForTorch(level, pos)) { - return false; - } - BlockState state = Blocks.TORCH.defaultBlockState(); - if (!state.canSurvive(level, pos)) { - return false; - } - level.setBlock(pos, state, 3); - level.playSound(null, pos, state.getSoundType().getPlaceSound(), SoundSource.BLOCKS, 1.0F, 1.0F); - return true; - } - - private static boolean placeWallTorch(ServerLevel level, BlockPos center, RandomSource random) { - List order = Direction.Plane.HORIZONTAL.shuffledCopy(random); - for (int dy = -1; dy <= 1; dy++) { - BlockPos candidate = center.offset(0, dy, 0); - if (!isOpenForTorch(level, candidate)) { - continue; - } - for (Direction wallSide : order) { - Direction facing = wallSide.getOpposite(); - BlockState state = Blocks.WALL_TORCH.defaultBlockState().setValue(BlockStateProperties.HORIZONTAL_FACING, facing); - if (!state.canSurvive(level, candidate)) { - continue; - } - level.setBlock(candidate, state, 3); - level.playSound(null, candidate, state.getSoundType().getPlaceSound(), SoundSource.BLOCKS, 1.0F, 1.0F); - return true; - } - } - return false; - } - - private static boolean isOpenForTorch(ServerLevel level, BlockPos pos) { - BlockState state = level.getBlockState(pos); - return (state.isAir() || state.canBeReplaced()) && level.getFluidState(pos).isEmpty(); - } -} diff --git a/common-1.20.1/src/main/resources/assets/antarchy/lang/de_de.json b/common-1.20.1/src/main/resources/assets/antarchy/lang/de_de.json index 9eecf3e6..737410dc 100644 --- a/common-1.20.1/src/main/resources/assets/antarchy/lang/de_de.json +++ b/common-1.20.1/src/main/resources/assets/antarchy/lang/de_de.json @@ -411,11 +411,6 @@ "tooltip.antarchy.reverie_bottle.capture": "Rechtsklicke mit einer Glasflasche auf eine Reverie, um sie einzufangen.", "tooltip.antarchy.reverie_bottle.release": "Rechtsklick, um die Reverie freizulassen.", "item.antarchy.gravity_gun": "Gravitationskanone", - "item.antarchy.miners_dream": "Traum des Bergmanns", - "tooltip.antarchy.miners_dream.line1": "Rechtsklick, um eine gewundene Höhle zu graben.", - "tooltip.antarchy.miners_dream.line2": "Lässt wertvolle Blöcke unberührt.", - "message.antarchy.miners_dream_disabled": "Der Traum des Bergmanns ist deaktiviert.", - "message.antarchy.miners_dream_busy": "Dein vorheriger Traum entfaltet sich noch.", "item.antarchy.diamond_minecart": "Die Diamantlore", "item.antarchy.cogwheel_pet": "Zahnrad-Haustier", "item.antarchy.fire_dragon_pet": "Feuerdrachen-Haustier", diff --git a/common-1.20.1/src/main/resources/assets/antarchy/lang/en_us.json b/common-1.20.1/src/main/resources/assets/antarchy/lang/en_us.json index 0ba1bb83..7b1c83d4 100644 --- a/common-1.20.1/src/main/resources/assets/antarchy/lang/en_us.json +++ b/common-1.20.1/src/main/resources/assets/antarchy/lang/en_us.json @@ -632,11 +632,6 @@ "tooltip.antarchy.reverie_bottle.release": "Right click to release the Reverie.", "item.antarchy.gravity_gun": "Gravity Gun", "item.antarchy.portal_gun": "Portal Gun", - "item.antarchy.miners_dream": "Miner's Dream", - "tooltip.antarchy.miners_dream.line1": "Use to carve a winding cave.", - "tooltip.antarchy.miners_dream.line2": "Leaves valuable blocks untouched.", - "message.antarchy.miners_dream_disabled": "The Miner's Dream is disabled.", - "message.antarchy.miners_dream_busy": "Your previous dream is still unfolding.", "item.antarchy.diamond_minecart": "The Diamond Minecart", "item.antarchy.cogwheel_pet": "Cogwheel Pet", "item.antarchy.fire_dragon_pet": "Fire Dragon Pet", diff --git a/common-1.20.1/src/main/resources/assets/antarchy/lang/es_es.json b/common-1.20.1/src/main/resources/assets/antarchy/lang/es_es.json index dd76c4b6..34305034 100644 --- a/common-1.20.1/src/main/resources/assets/antarchy/lang/es_es.json +++ b/common-1.20.1/src/main/resources/assets/antarchy/lang/es_es.json @@ -411,11 +411,6 @@ "tooltip.antarchy.reverie_bottle.capture": "Haz clic derecho en una Reverie con una botella de cristal para guardarla.", "tooltip.antarchy.reverie_bottle.release": "Clic derecho para liberar a la Reverie.", "item.antarchy.gravity_gun": "Pistola gravitatoria", - "item.antarchy.miners_dream": "Sueño del minero", - "tooltip.antarchy.miners_dream.line1": "Clic derecho para excavar una cueva sinuosa.", - "tooltip.antarchy.miners_dream.line2": "Deja intactos los bloques valiosos.", - "message.antarchy.miners_dream_disabled": "El Sueño del minero está desactivado.", - "message.antarchy.miners_dream_busy": "Tu sueño anterior todavía se está desarrollando.", "item.antarchy.diamond_minecart": "La vagoneta de diamante", "item.antarchy.cogwheel_pet": "Mascota engranaje", "item.antarchy.fire_dragon_pet": "Mascota de dragón de fuego", diff --git a/common-1.20.1/src/main/resources/assets/antarchy/lang/fr_fr.json b/common-1.20.1/src/main/resources/assets/antarchy/lang/fr_fr.json index 1bb5b8ef..0a7b39e0 100644 --- a/common-1.20.1/src/main/resources/assets/antarchy/lang/fr_fr.json +++ b/common-1.20.1/src/main/resources/assets/antarchy/lang/fr_fr.json @@ -411,11 +411,6 @@ "tooltip.antarchy.reverie_bottle.capture": "Faites un clic droit sur une Reverie avec une fiole en verre pour la capturer.", "tooltip.antarchy.reverie_bottle.release": "Clic droit pour libérer la Reverie.", "item.antarchy.gravity_gun": "Canon à gravité", - "item.antarchy.miners_dream": "Rêve du mineur", - "tooltip.antarchy.miners_dream.line1": "Clic droit pour creuser une grotte sinueuse.", - "tooltip.antarchy.miners_dream.line2": "Laisse les blocs précieux intacts.", - "message.antarchy.miners_dream_disabled": "Le Rêve du mineur est désactivé.", - "message.antarchy.miners_dream_busy": "Votre rêve précédent est encore en train de se déployer.", "item.antarchy.diamond_minecart": "Le wagonnet de diamant", "item.antarchy.cogwheel_pet": "Familier engrenage", "item.antarchy.fire_dragon_pet": "Familier dragon de feu", diff --git a/common-1.20.1/src/main/resources/assets/antarchy/lang/it_it.json b/common-1.20.1/src/main/resources/assets/antarchy/lang/it_it.json index e5ceab2e..69fa31ce 100644 --- a/common-1.20.1/src/main/resources/assets/antarchy/lang/it_it.json +++ b/common-1.20.1/src/main/resources/assets/antarchy/lang/it_it.json @@ -411,11 +411,6 @@ "tooltip.antarchy.reverie_bottle.capture": "Fai clic destro su una Reverie con una bottiglia di vetro per catturarla.", "tooltip.antarchy.reverie_bottle.release": "Fai clic destro per liberare la Reverie.", "item.antarchy.gravity_gun": "Cannone gravitazionale", - "item.antarchy.miners_dream": "Sogno del minatore", - "tooltip.antarchy.miners_dream.line1": "Fai clic destro per scavare una grotta tortuosa.", - "tooltip.antarchy.miners_dream.line2": "Lascia intatti i blocchi preziosi.", - "message.antarchy.miners_dream_disabled": "Il Sogno del minatore è disattivato.", - "message.antarchy.miners_dream_busy": "Il tuo sogno precedente si sta ancora dispiegando.", "item.antarchy.diamond_minecart": "Il carrello di diamante", "item.antarchy.cogwheel_pet": "Famiglio ingranaggio", "item.antarchy.fire_dragon_pet": "Famiglio drago di fuoco", diff --git a/common-1.20.1/src/main/resources/assets/antarchy/lang/ja_jp.json b/common-1.20.1/src/main/resources/assets/antarchy/lang/ja_jp.json index 664b7767..1d17dbc3 100644 --- a/common-1.20.1/src/main/resources/assets/antarchy/lang/ja_jp.json +++ b/common-1.20.1/src/main/resources/assets/antarchy/lang/ja_jp.json @@ -411,11 +411,6 @@ "tooltip.antarchy.reverie_bottle.capture": "ガラス瓶を持ってレヴェリーを右クリックすると収納できます。", "tooltip.antarchy.reverie_bottle.release": "右クリックでレヴェリーを解放します。", "item.antarchy.gravity_gun": "重力銃", - "item.antarchy.miners_dream": "採掘者の夢", - "tooltip.antarchy.miners_dream.line1": "右クリックで曲がりくねった洞窟を掘ります。", - "tooltip.antarchy.miners_dream.line2": "貴重なブロックはそのまま残します。", - "message.antarchy.miners_dream_disabled": "採掘者の夢は無効になっています。", - "message.antarchy.miners_dream_busy": "前の夢はまだ展開中です。", "item.antarchy.diamond_minecart": "ダイヤモンドのトロッコ", "item.antarchy.cogwheel_pet": "歯車ペット", "item.antarchy.fire_dragon_pet": "ファイアドラゴンのペット", diff --git a/common-1.20.1/src/main/resources/assets/antarchy/lang/ko_kr.json b/common-1.20.1/src/main/resources/assets/antarchy/lang/ko_kr.json index 4d74cf03..7fc4ee1e 100644 --- a/common-1.20.1/src/main/resources/assets/antarchy/lang/ko_kr.json +++ b/common-1.20.1/src/main/resources/assets/antarchy/lang/ko_kr.json @@ -411,11 +411,6 @@ "tooltip.antarchy.reverie_bottle.capture": "유리병으로 레버리를 우클릭하여 담습니다.", "tooltip.antarchy.reverie_bottle.release": "우클릭하여 레버리를 풀어줍니다.", "item.antarchy.gravity_gun": "중력총", - "item.antarchy.miners_dream": "광부의 꿈", - "tooltip.antarchy.miners_dream.line1": "우클릭하여 구불구불한 동굴을 뚫습니다.", - "tooltip.antarchy.miners_dream.line2": "귀중한 블록은 건드리지 않습니다.", - "message.antarchy.miners_dream_disabled": "광부의 꿈이 비활성화되어 있습니다.", - "message.antarchy.miners_dream_busy": "이전 꿈이 아직 펼쳐지고 있습니다.", "item.antarchy.diamond_minecart": "다이아몬드 광산 수레", "item.antarchy.cogwheel_pet": "톱니바퀴 펫", "item.antarchy.fire_dragon_pet": "화염 드래곤 펫", diff --git a/common-1.20.1/src/main/resources/assets/antarchy/lang/pt_br.json b/common-1.20.1/src/main/resources/assets/antarchy/lang/pt_br.json index 55cf70c9..3ae07a99 100644 --- a/common-1.20.1/src/main/resources/assets/antarchy/lang/pt_br.json +++ b/common-1.20.1/src/main/resources/assets/antarchy/lang/pt_br.json @@ -411,11 +411,6 @@ "tooltip.antarchy.reverie_bottle.capture": "Clique com o botão direito em uma Reverie com um frasco de vidro para guardá-la.", "tooltip.antarchy.reverie_bottle.release": "Clique com o botão direito para soltar a Reverie.", "item.antarchy.gravity_gun": "Arma Gravitacional", - "item.antarchy.miners_dream": "Sonho do Minerador", - "tooltip.antarchy.miners_dream.line1": "Clique com o botão direito para escavar uma caverna sinuosa.", - "tooltip.antarchy.miners_dream.line2": "Deixa blocos valiosos intactos.", - "message.antarchy.miners_dream_disabled": "O Sonho do Minerador está desativado.", - "message.antarchy.miners_dream_busy": "Seu sonho anterior ainda está se desenrolando.", "item.antarchy.diamond_minecart": "O Carrinho de Diamante", "item.antarchy.cogwheel_pet": "Mascote de Engrenagem", "item.antarchy.fire_dragon_pet": "Mascote de Dragão de Fogo", diff --git a/common-1.20.1/src/main/resources/assets/antarchy/lang/ru_ru.json b/common-1.20.1/src/main/resources/assets/antarchy/lang/ru_ru.json index 1bc3167c..fb7a6071 100644 --- a/common-1.20.1/src/main/resources/assets/antarchy/lang/ru_ru.json +++ b/common-1.20.1/src/main/resources/assets/antarchy/lang/ru_ru.json @@ -387,11 +387,6 @@ "tooltip.antarchy.reverie_bottle.capture": "Нажмите ПКМ стеклянной бутылочкой по грёзе, чтобы поймать её.", "tooltip.antarchy.reverie_bottle.release": "Нажмите ПКМ, чтобы выпустить грёзу.", "item.antarchy.gravity_gun": "Гравитационная пушка", - "item.antarchy.miners_dream": "Мечта шахтёра", - "tooltip.antarchy.miners_dream.line1": "Нажмите ПКМ, чтобы вырезать извилистую пещеру.", - "tooltip.antarchy.miners_dream.line2": "Оставляет ценные блоки нетронутыми.", - "message.antarchy.miners_dream_disabled": "Мечта шахтёра отключена.", - "message.antarchy.miners_dream_busy": "Ваша предыдущая мечта всё ещё воплощается.", "item.antarchy.diamond_minecart": "Алмазная вагонетка", "item.antarchy.cogwheel_pet": "Шестерёнка-питомец", "item.antarchy.fire_dragon_pet": "Огненный дракон-питомец", diff --git a/common-1.20.1/src/main/resources/assets/antarchy/lang/zh_cn.json b/common-1.20.1/src/main/resources/assets/antarchy/lang/zh_cn.json index bde52c13..e3acfc40 100644 --- a/common-1.20.1/src/main/resources/assets/antarchy/lang/zh_cn.json +++ b/common-1.20.1/src/main/resources/assets/antarchy/lang/zh_cn.json @@ -411,11 +411,6 @@ "tooltip.antarchy.reverie_bottle.capture": "手持玻璃瓶右键雷弗里即可将其收纳。", "tooltip.antarchy.reverie_bottle.release": "右键释放雷弗里。", "item.antarchy.gravity_gun": "重力枪", - "item.antarchy.miners_dream": "矿工之梦", - "tooltip.antarchy.miners_dream.line1": "右键挖出一条蜿蜒洞穴。", - "tooltip.antarchy.miners_dream.line2": "不会破坏贵重方块。", - "message.antarchy.miners_dream_disabled": "矿工之梦已禁用。", - "message.antarchy.miners_dream_busy": "你之前的梦仍在展开。", "item.antarchy.diamond_minecart": "钻石矿车", "item.antarchy.cogwheel_pet": "齿轮宠物", "item.antarchy.fire_dragon_pet": "火龙宠物", diff --git a/common-1.20.1/src/main/resources/assets/antarchy/lang/zh_tw.json b/common-1.20.1/src/main/resources/assets/antarchy/lang/zh_tw.json index 2c3a6435..971be7e9 100644 --- a/common-1.20.1/src/main/resources/assets/antarchy/lang/zh_tw.json +++ b/common-1.20.1/src/main/resources/assets/antarchy/lang/zh_tw.json @@ -411,11 +411,6 @@ "tooltip.antarchy.reverie_bottle.capture": "手持玻璃瓶右鍵雷弗裡即可將其收納。", "tooltip.antarchy.reverie_bottle.release": "右鍵釋放雷弗裡。", "item.antarchy.gravity_gun": "重力槍", - "item.antarchy.miners_dream": "礦工之夢", - "tooltip.antarchy.miners_dream.line1": "右鍵挖出一條蜿蜒洞穴。", - "tooltip.antarchy.miners_dream.line2": "不會破壞貴重方塊。", - "message.antarchy.miners_dream_disabled": "礦工之夢已禁用。", - "message.antarchy.miners_dream_busy": "你之前的夢仍在展開。", "item.antarchy.diamond_minecart": "鑽石礦車", "item.antarchy.cogwheel_pet": "齒輪寵物", "item.antarchy.fire_dragon_pet": "火龍寵物", diff --git a/common-1.20.1/src/main/resources/assets/antarchy/models/item/miners_dream.json b/common-1.20.1/src/main/resources/assets/antarchy/models/item/miners_dream.json deleted file mode 100644 index c7a0f166..00000000 --- a/common-1.20.1/src/main/resources/assets/antarchy/models/item/miners_dream.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "minecraft:item/generated", - "textures": { - "layer0": "antarchy:item/miners_dream" - } -} diff --git a/common-1.20.1/src/main/resources/assets/antarchy/textures/item/miners_dream.png b/common-1.20.1/src/main/resources/assets/antarchy/textures/item/miners_dream.png deleted file mode 100644 index d8371b7b..00000000 Binary files a/common-1.20.1/src/main/resources/assets/antarchy/textures/item/miners_dream.png and /dev/null differ diff --git a/common-1.20.1/src/main/resources/data/antarchy/recipe/miners_dream.json.disabled b/common-1.20.1/src/main/resources/data/antarchy/recipe/miners_dream.json.disabled deleted file mode 100644 index b35b1c80..00000000 --- a/common-1.20.1/src/main/resources/data/antarchy/recipe/miners_dream.json.disabled +++ /dev/null @@ -1,19 +0,0 @@ -{ - "type": "minecraft:crafting_shaped", - "category": "misc", - "pattern": [ - "GGG", - "RDR", - "TTT" - ], - "key": { - "G": { "item": "antarchy:triffid_goo" }, - "R": { "item": "minecraft:redstone_block" }, - "D": { "item": "minecraft:diamond_pickaxe" }, - "T": { "item": "minecraft:tnt" } - }, - "result": { - "id": "antarchy:miners_dream", - "count": 1 - } -} diff --git a/common-1.20.1/src/main/resources/data/antarchy/recipes/miners_dream.json.disabled b/common-1.20.1/src/main/resources/data/antarchy/recipes/miners_dream.json.disabled deleted file mode 100644 index 0fba3562..00000000 --- a/common-1.20.1/src/main/resources/data/antarchy/recipes/miners_dream.json.disabled +++ /dev/null @@ -1,19 +0,0 @@ -{ - "type": "minecraft:crafting_shaped", - "category": "misc", - "pattern": [ - "GGG", - "RDR", - "TTT" - ], - "key": { - "G": { "item": "antarchy:triffid_goo" }, - "R": { "item": "minecraft:redstone_block" }, - "D": { "item": "minecraft:diamond_pickaxe" }, - "T": { "item": "minecraft:tnt" } - }, - "result": { - "item": "antarchy:miners_dream", - "count": 1 - } -} diff --git a/common-1.20.1/src/main/resources/data/antarchy/tags/block/miners_dream_carvable.json b/common-1.20.1/src/main/resources/data/antarchy/tags/block/miners_dream_carvable.json deleted file mode 100644 index 6f7d26ee..00000000 --- a/common-1.20.1/src/main/resources/data/antarchy/tags/block/miners_dream_carvable.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "replace": false, - "values": [ - "#minecraft:base_stone_overworld", - "#minecraft:base_stone_nether", - "minecraft:stone", - "minecraft:deepslate", - "minecraft:granite", - "minecraft:diorite", - "minecraft:andesite", - "minecraft:tuff", - "minecraft:calcite", - "minecraft:netherrack", - "minecraft:blackstone", - "minecraft:basalt", - "minecraft:smooth_basalt", - "minecraft:end_stone", - "minecraft:dirt", - "minecraft:coarse_dirt", - "minecraft:rooted_dirt", - "minecraft:podzol", - "minecraft:mycelium", - "minecraft:gravel", - "minecraft:sand", - "minecraft:red_sand", - "minecraft:sandstone", - "minecraft:red_sandstone", - "minecraft:clay", - "minecraft:soul_sand", - "minecraft:soul_soil", - "antarchy:nyxite", - "antarchy:pale_nyxite", - "antarchy:dream_sand", - "antarchy:dream_sandstone" - ] -} diff --git a/common-1.20.1/src/main/resources/data/antarchy/tags/blocks/miners_dream_carvable.json b/common-1.20.1/src/main/resources/data/antarchy/tags/blocks/miners_dream_carvable.json deleted file mode 100644 index 6f7d26ee..00000000 --- a/common-1.20.1/src/main/resources/data/antarchy/tags/blocks/miners_dream_carvable.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "replace": false, - "values": [ - "#minecraft:base_stone_overworld", - "#minecraft:base_stone_nether", - "minecraft:stone", - "minecraft:deepslate", - "minecraft:granite", - "minecraft:diorite", - "minecraft:andesite", - "minecraft:tuff", - "minecraft:calcite", - "minecraft:netherrack", - "minecraft:blackstone", - "minecraft:basalt", - "minecraft:smooth_basalt", - "minecraft:end_stone", - "minecraft:dirt", - "minecraft:coarse_dirt", - "minecraft:rooted_dirt", - "minecraft:podzol", - "minecraft:mycelium", - "minecraft:gravel", - "minecraft:sand", - "minecraft:red_sand", - "minecraft:sandstone", - "minecraft:red_sandstone", - "minecraft:clay", - "minecraft:soul_sand", - "minecraft:soul_soil", - "antarchy:nyxite", - "antarchy:pale_nyxite", - "antarchy:dream_sand", - "antarchy:dream_sandstone" - ] -} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/config/AntarchyConfigCatalog.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/config/AntarchyConfigCatalog.java index 123e04b1..45def0a3 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/config/AntarchyConfigCatalog.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/config/AntarchyConfigCatalog.java @@ -137,6 +137,14 @@ public final class AntarchyConfigCatalog { || name.equals("elythiaPortalEnabled") || name.equals("thoraxisPortalEnabled") || name.equals("cavarynPortalEnabled") + || name.startsWith("portalGunCanFireThrough") + || name.startsWith("portalGunSeeThrough") + || name.startsWith("portalGunFancy") + || name.equals("portalGunCanPortalsResizeWhenCreated") + || name.equals("portalGunCanPortalProjectilesChunkload") + || name.equals("portalGunMaxShootDistance") + || name.equals("portalGunEntityGrabWeightBase") + || name.equals("portalGunIndicatorSize") || name.equals("hushweedSporeLifetimeSeconds") || name.equals("elythiaFireflyParticlesEnabled") || name.equals("duplicatorTreeEnabled") @@ -177,7 +185,6 @@ public final class AntarchyConfigCatalog { || name.equals("invertProjectilesFromInvertedPlayers") || name.startsWith("gravityGun") || name.startsWith("critterCage") - || name.startsWith("minersDream") || name.equals("potentNyxiteInvertedDurationSeconds") || name.equals("corneaEarNightVisionSeconds") || name.startsWith("american") diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/config/AntarchySettings.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/config/AntarchySettings.java index 6ac2de62..b8207c6d 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/config/AntarchySettings.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/config/AntarchySettings.java @@ -167,11 +167,6 @@ public final class AntarchySettings { private static double gravityGunMaxHoldDistance = 10.0D; private static double critterCageMaxCapturableWidth = 1.65D; private static double critterCageMaxCapturableHeight = 1.65D; - private static boolean minersDreamEnabled = true; - private static int minersDreamMinimumRange = 48; - private static int minersDreamMaximumRange = 96; - private static int minersDreamTorchSpacing = 10; - private static int minersDreamBlocksPerTick = 150; private static double lucidAttackRange = 9.0D; private static double lucidPearlInvertedDurationSeconds = 6.0D; private static int corneaEarNightVisionSeconds = 15; @@ -554,6 +549,15 @@ public final class AntarchySettings { private static boolean elythiaPortalEnabled = true; private static boolean thoraxisPortalEnabled = true; private static boolean cavarynPortalEnabled = true; + private static boolean portalGunCanFireThroughGlass = false; + private static boolean portalGunCanFireThroughLiquid = false; + private static boolean portalGunCanPortalsResizeWhenCreated = true; + private static boolean portalGunCanPortalProjectilesChunkload = true; + private static boolean portalGunSeeThroughPortals = true; + private static boolean portalGunFancyPortals = true; + private static int portalGunIndicatorSize = 30; + private static int portalGunMaxShootDistance = 10000; + private static int portalGunEntityGrabWeightBase = 100; private static boolean elythiaFireflyParticlesEnabled = true; private static int bloodCrystalArmorShieldRechargeTicks = 600; private static int bloodCrystalAppleShieldCount = 2; @@ -1251,26 +1255,6 @@ public final class AntarchySettings { return critterCageMaxCapturableHeight; } - public static boolean minersDreamEnabled() { - return minersDreamEnabled; - } - - public static int minersDreamMinimumRange() { - return minersDreamMinimumRange; - } - - public static int minersDreamMaximumRange() { - return minersDreamMaximumRange; - } - - public static int minersDreamTorchSpacing() { - return minersDreamTorchSpacing; - } - - public static int minersDreamBlocksPerTick() { - return minersDreamBlocksPerTick; - } - public static void setDisableInfinityBookPortalCreation(boolean value) { disableInfinityBookPortalCreation = value; } @@ -1327,11 +1311,56 @@ public final class AntarchySettings { cavarynPortalEnabled = value; } + public static void setPortalGunCanFireThroughGlass(boolean value) { + portalGunCanFireThroughGlass = value; + } + + public static void setPortalGunCanFireThroughLiquid(boolean value) { + portalGunCanFireThroughLiquid = value; + } + + public static void setPortalGunCanPortalsResizeWhenCreated(boolean value) { + portalGunCanPortalsResizeWhenCreated = value; + } + + public static void setPortalGunCanPortalProjectilesChunkload(boolean value) { + portalGunCanPortalProjectilesChunkload = value; + } + + public static void setPortalGunSeeThroughPortals(boolean value) { + portalGunSeeThroughPortals = value; + } + + public static void setPortalGunFancyPortals(boolean value) { + portalGunFancyPortals = value; + } + + public static void setPortalGunIndicatorSize(int value) { + portalGunIndicatorSize = Math.max(0, Math.min(100, value)); + } + + public static void setPortalGunMaxShootDistance(int value) { + portalGunMaxShootDistance = Math.max(1, value); + } + + public static void setPortalGunEntityGrabWeightBase(int value) { + portalGunEntityGrabWeightBase = Math.max(0, value); + } + public static boolean permanentPortalsEnabled() { return permanentPortalsEnabled; } public static boolean permanentPortalsFlintAndSteelEnabled() { return permanentPortalsFlintAndSteelEnabled; } public static boolean elythiaPortalEnabled() { return elythiaPortalEnabled; } public static boolean thoraxisPortalEnabled() { return thoraxisPortalEnabled; } public static boolean cavarynPortalEnabled() { return cavarynPortalEnabled; } + public static boolean portalGunCanFireThroughGlass() { return portalGunCanFireThroughGlass; } + public static boolean portalGunCanFireThroughLiquid() { return portalGunCanFireThroughLiquid; } + public static boolean portalGunCanPortalsResizeWhenCreated() { return portalGunCanPortalsResizeWhenCreated; } + public static boolean portalGunCanPortalProjectilesChunkload() { return portalGunCanPortalProjectilesChunkload; } + public static boolean portalGunSeeThroughPortals() { return portalGunSeeThroughPortals; } + public static boolean portalGunFancyPortals() { return portalGunFancyPortals; } + public static int portalGunIndicatorSize() { return portalGunIndicatorSize; } + public static int portalGunMaxShootDistance() { return portalGunMaxShootDistance; } + public static int portalGunEntityGrabWeightBase() { return portalGunEntityGrabWeightBase; } public static void setBrownAntRequiresReagent(boolean value) { brownAntRequiresReagent = value; @@ -1809,26 +1838,6 @@ public final class AntarchySettings { critterCageMaxCapturableHeight = value; } - public static void setMinersDreamEnabled(boolean value) { - minersDreamEnabled = value; - } - - public static void setMinersDreamMinimumRange(int value) { - minersDreamMinimumRange = value; - } - - public static void setMinersDreamMaximumRange(int value) { - minersDreamMaximumRange = value; - } - - public static void setMinersDreamTorchSpacing(int value) { - minersDreamTorchSpacing = value; - } - - public static void setMinersDreamBlocksPerTick(int value) { - minersDreamBlocksPerTick = value; - } - public static double lucidAttackRange() { return lucidAttackRange; } diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/AntarchyTags.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/AntarchyTags.java index 42b4b92e..63c0a129 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/AntarchyTags.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/AntarchyTags.java @@ -41,7 +41,6 @@ public final class AntarchyTags { public static final TagKey CAVARYN_PORTAL_FRAMES = TagKey.create(Registries.BLOCK, id("cavaryn_portal_frames")); public static final TagKey RAINBOW_PORTAL_FRAMES = TagKey.create(Registries.BLOCK, id("rainbow_portal_frames")); public static final TagKey ROLLY_CAVERN_CARVEABLE = TagKey.create(Registries.BLOCK, id("rolly_cavern_carveable")); - public static final TagKey MINERS_DREAM_CARVABLE = TagKey.create(Registries.BLOCK, id("miners_dream_carvable")); public static final TagKey BLUESTONE_COMPONENTS = TagKey.create(Registries.BLOCK, id("bluestone_components")); public static final TagKey BLUESTONE_CONNECTABLE = TagKey.create(Registries.BLOCK, id("bluestone_connectable")); public static final TagKey ANTIMETAL_INVERSION_BLOCKS = TagKey.create(Registries.BLOCK, id("antimetal_inversion_blocks")); diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/block/entity/PortalGunPortalBaseBlockEntity.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/block/entity/PortalGunPortalBaseBlockEntity.java index be1b9709..c3a3483b 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/block/entity/PortalGunPortalBaseBlockEntity.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/block/entity/PortalGunPortalBaseBlockEntity.java @@ -1,6 +1,8 @@ package com.craisinlord.antarchy.content.block.entity; import com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity; +import java.util.EnumMap; +import java.util.Map; import java.util.UUID; import java.util.function.Supplier; import net.minecraft.core.BlockPos; @@ -14,7 +16,8 @@ import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.state.BlockState; -public class PortalGunPortalBaseBlockEntity extends BlockEntity { +public class PortalGunPortalBaseBlockEntity extends BlockEntity implements PortalGunPortalCellAccess { + private final Map faceRecords = new EnumMap<>(net.minecraft.core.Direction.class); private UUID ownerId; private UUID portalId; private PortalGunPortalEntity.PortalSide side = PortalGunPortalEntity.PortalSide.BLUE; @@ -26,6 +29,11 @@ public class PortalGunPortalBaseBlockEntity extends BlockEntity { super(typeSupplier.get(), pos, state); } + @Override + public Map portalGun$getFaceRecords() { + return this.faceRecords; + } + public void configure(UUID ownerId, UUID portalId, PortalGunPortalEntity.PortalSide side, BlockPos masterPos) { this.ownerId = ownerId; this.portalId = portalId; @@ -61,6 +69,11 @@ public class PortalGunPortalBaseBlockEntity extends BlockEntity { public void updatePair(UUID linkedPortalId, int pairTime) { this.linkedPortalId = linkedPortalId; this.pairTime = pairTime; + for (PortalGunPortalFaceRecord record : java.util.List.copyOf(this.faceRecords.values())) { + if (record.portalId().equals(this.portalId)) { + this.portalGun$putFaceRecord(record.withPair(linkedPortalId, pairTime)); + } + } this.setChanged(); } @@ -69,11 +82,20 @@ public class PortalGunPortalBaseBlockEntity extends BlockEntity { } public void onBroken() { - if (!(this.level instanceof ServerLevel serverLevel) || this.portalId == null) { + if (!(this.level instanceof ServerLevel serverLevel)) { return; } - if (serverLevel.getEntity(this.portalId) instanceof PortalGunPortalEntity portal && !portal.isRemoved()) { - portal.discard(); + java.util.Set portalIds = new java.util.HashSet<>(); + if (this.portalId != null) { + portalIds.add(this.portalId); + } + for (PortalGunPortalFaceRecord record : this.faceRecords.values()) { + portalIds.add(record.portalId()); + } + for (UUID id : portalIds) { + if (serverLevel.getEntity(id) instanceof PortalGunPortalEntity portal && !portal.isRemoved()) { + portal.discard(); + } } } @@ -81,15 +103,28 @@ public class PortalGunPortalBaseBlockEntity extends BlockEntity { if (((level.getGameTime() + pos.asLong()) & 15L) != 0L) { return; } + if (level.getBlockEntity(blockEntity.masterPos) instanceof PortalGunPortalMasterBlockEntity master + && master.matches(blockEntity.ownerId, blockEntity.portalId, blockEntity.side)) { + PortalGunPortalFaceRecord masterRecord = master.portalGun$getFaceRecord(master.getFacing(), blockEntity.portalId); + if (masterRecord != null && blockEntity.portalGun$getFaceRecord(masterRecord.face(), masterRecord.portalId()) == null) { + blockEntity.portalGun$putFaceRecord(masterRecord.withMaster(false)); + } + } + blockEntity.portalGun$restoreMasterFaces(level, pos); + if (blockEntity.portalId != null + && !(level.getEntity(blockEntity.portalId) instanceof PortalGunPortalEntity) + && blockEntity.faceRecords.values().stream().anyMatch(record -> !record.portalId().equals(blockEntity.portalId))) { + return; + } if (!(level.getBlockEntity(blockEntity.masterPos) instanceof PortalGunPortalMasterBlockEntity master) || !master.matches(blockEntity.ownerId, blockEntity.portalId, blockEntity.side) - || !pos.equals(master.getBasePos())) { + || !master.containsPortalSpot(pos)) { level.removeBlock(pos, false); return; } if (!(level.getEntity(blockEntity.portalId) instanceof PortalGunPortalEntity portal) || portal.isRemoved() - || !pos.equals(portal.getBasePos()) + || !portal.containsPortalSpot(pos) || blockEntity.ownerId == null || !blockEntity.ownerId.equals(portal.getOwnerId()) || blockEntity.side != portal.getPortalSide()) { @@ -123,6 +158,7 @@ public class PortalGunPortalBaseBlockEntity extends BlockEntity { tag.putInt("MasterY", this.masterPos.getY()); tag.putInt("MasterZ", this.masterPos.getZ()); tag.putInt("PairTime", this.pairTime); + this.portalGun$saveFaceRecords(tag); } @Override @@ -134,6 +170,7 @@ public class PortalGunPortalBaseBlockEntity extends BlockEntity { this.side = tag.getInt("Side") == PortalGunPortalEntity.PortalSide.ORANGE.ordinal() ? PortalGunPortalEntity.PortalSide.ORANGE : PortalGunPortalEntity.PortalSide.BLUE; this.masterPos = new BlockPos(tag.getInt("MasterX"), tag.getInt("MasterY"), tag.getInt("MasterZ")); this.pairTime = tag.getInt("PairTime"); + this.portalGun$loadFaceRecords(tag); } @Override diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/block/entity/PortalGunPortalCellAccess.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/block/entity/PortalGunPortalCellAccess.java new file mode 100644 index 00000000..14c30c4c --- /dev/null +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/block/entity/PortalGunPortalCellAccess.java @@ -0,0 +1,118 @@ +package com.craisinlord.antarchy.content.block.entity; + +import com.craisinlord.antarchy.content.portalgun.PortalGunPlacement; +import com.craisinlord.antarchy.content.portalgun.PortalGunSavedData; +import java.util.Map; +import java.util.UUID; +import net.minecraft.core.Direction; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.Tag; +import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerLevel; + +public interface PortalGunPortalCellAccess { + Map portalGun$getFaceRecords(); + + default PortalGunPortalFaceRecord portalGun$getFaceRecord(Direction face) { + return this.portalGun$getFaceRecords().get(face); + } + + default PortalGunPortalFaceRecord portalGun$getFaceRecord(Direction face, UUID portalId) { + PortalGunPortalFaceRecord record = this.portalGun$getFaceRecord(face); + return record != null && record.portalId().equals(portalId) ? record : null; + } + + default boolean portalGun$putFaceRecord(PortalGunPortalFaceRecord record) { + PortalGunPortalFaceRecord existing = this.portalGun$getFaceRecord(record.face()); + if (existing != null + && (!existing.ownerId().equals(record.ownerId()) + || !java.util.Objects.equals(existing.channelName(), record.channelName()) + || existing.side() != record.side())) { + return false; + } + this.portalGun$getFaceRecords().put(record.face(), record); + if (this instanceof net.minecraft.world.level.block.entity.BlockEntity blockEntity) { + blockEntity.setChanged(); + } + return true; + } + + default PortalGunPortalFaceRecord portalGun$removeFaceRecord(Direction face, UUID portalId) { + PortalGunPortalFaceRecord existing = this.portalGun$getFaceRecord(face, portalId); + if (existing == null) { + return null; + } + PortalGunPortalFaceRecord removed = this.portalGun$getFaceRecords().remove(face); + if (this instanceof net.minecraft.world.level.block.entity.BlockEntity blockEntity) { + blockEntity.setChanged(); + } + return removed; + } + + default void portalGun$updateFacePair(UUID portalId, UUID linkedPortalId, int pairTime) { + for (PortalGunPortalFaceRecord record : java.util.List.copyOf(this.portalGun$getFaceRecords().values())) { + if (record.portalId().equals(portalId)) { + this.portalGun$putFaceRecord(record.withPair(linkedPortalId, pairTime)); + } + } + } + + default void portalGun$restoreMasterFaces(ServerLevel level, BlockPos pos) { + for (PortalGunPortalFaceRecord record : java.util.List.copyOf(this.portalGun$getFaceRecords().values())) { + if (record.ownerId() == null || record.portalId() == null) { + continue; + } + if (!PortalGunSavedData.isRegistered(level, record.ownerId(), record.gunId(), record.channelName(), record.side(), record.portalId())) { + this.portalGun$removeFaceRecord(record.face(), record.portalId()); + continue; + } + if (!record.master() || !record.masterPos().equals(pos)) { + continue; + } + boolean complete = !record.portalSpots().isEmpty() && record.portalSpots().getFirst().equals(record.masterPos()); + for (int i = 0; complete && i < record.portalSpots().size(); i++) { + BlockPos spot = record.portalSpots().get(i); + BlockPos support = spot.relative(record.face().getOpposite()); + if (!level.hasChunkAt(spot) || !level.hasChunkAt(support) + || !level.getBlockState(support).isFaceSturdy(level, support, record.face()) + || !(level.getBlockEntity(spot) instanceof PortalGunPortalCellAccess cell)) { + complete = false; + continue; + } + PortalGunPortalFaceRecord footprintRecord = cell.portalGun$getFaceRecord(record.face(), record.portalId()); + complete = footprintRecord != null && footprintRecord.master() == (i == 0); + } + complete = complete && PortalGunPlacement.isRectangularFootprint( + record.portalSpots().toArray(BlockPos[]::new), + record.face(), + record.upAxis(), + record.portalWidth(), + record.portalHeight() + ); + if (complete) { + PortalGunPortalMasterBlockEntity.restorePortalEntity(level, record); + } + } + } + + default void portalGun$saveFaceRecords(CompoundTag tag) { + ListTag faces = new ListTag(); + for (PortalGunPortalFaceRecord record : this.portalGun$getFaceRecords().values()) { + faces.add(record.save()); + } + tag.put("PortalFaces", faces); + } + + default void portalGun$loadFaceRecords(CompoundTag tag) { + Map records = this.portalGun$getFaceRecords(); + records.clear(); + ListTag faces = tag.getList("PortalFaces", Tag.TAG_COMPOUND); + for (int i = 0; i < faces.size(); i++) { + PortalGunPortalFaceRecord record = PortalGunPortalFaceRecord.load(faces.getCompound(i)); + if (record != null) { + records.put(record.face(), record); + } + } + } +} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/block/entity/PortalGunPortalFaceRecord.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/block/entity/PortalGunPortalFaceRecord.java new file mode 100644 index 00000000..806568b1 --- /dev/null +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/block/entity/PortalGunPortalFaceRecord.java @@ -0,0 +1,151 @@ +package com.craisinlord.antarchy.content.block.entity; + +import com.craisinlord.antarchy.content.portalgun.PortalGunPlacement; +import com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.UUID; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.Tag; + +public record PortalGunPortalFaceRecord( + UUID ownerId, + String ownerIdentity, + UUID gunId, + String channelName, + UUID portalId, + UUID linkedPortalId, + PortalGunPortalEntity.PortalSide side, + Direction face, + Direction upAxis, + BlockPos masterPos, + BlockPos basePos, + List portalSpots, + Set compensatedSpots, + int pairTime, + int portalWidth, + int portalHeight, + boolean master +) { + public PortalGunPortalFaceRecord { + masterPos = masterPos.immutable(); + basePos = basePos.immutable(); + portalSpots = portalSpots.stream().map(BlockPos::immutable).toList(); + compensatedSpots = Set.copyOf(compensatedSpots); + } + + public PortalGunPlacement placement() { + return PortalGunPlacement.fromStored(this.face, this.upAxis, this.masterPos, this.basePos, this.portalSpots.toArray(BlockPos[]::new), this.compensatedSpots, this.portalWidth, this.portalHeight); + } + + public PortalGunPortalFaceRecord withPair(UUID linkedPortalId, int pairTime) { + return new PortalGunPortalFaceRecord(this.ownerId, this.ownerIdentity, this.gunId, this.channelName, this.portalId, linkedPortalId, this.side, this.face, this.upAxis, this.masterPos, this.basePos, this.portalSpots, this.compensatedSpots, pairTime, this.portalWidth, this.portalHeight, this.master); + } + + public PortalGunPortalFaceRecord withMaster(boolean master) { + return new PortalGunPortalFaceRecord(this.ownerId, this.ownerIdentity, this.gunId, this.channelName, this.portalId, this.linkedPortalId, this.side, this.face, this.upAxis, this.masterPos, this.basePos, this.portalSpots, this.compensatedSpots, this.pairTime, this.portalWidth, this.portalHeight, master); + } + + public CompoundTag save() { + CompoundTag tag = new CompoundTag(); + tag.putUUID("OwnerId", this.ownerId); + tag.putString("OwnerIdentity", this.ownerIdentity == null ? "" : this.ownerIdentity); + if (this.gunId != null) { + tag.putUUID("GunId", this.gunId); + } + tag.putString("ChannelName", this.channelName == null ? "" : this.channelName); + tag.putUUID("PortalId", this.portalId); + if (this.linkedPortalId != null) { + tag.putUUID("LinkedPortalId", this.linkedPortalId); + } + tag.putInt("Side", this.side.ordinal()); + tag.putInt("Face", this.face.get3DDataValue()); + tag.putInt("UpAxis", this.upAxis.get3DDataValue()); + putPos(tag, "Master", this.masterPos); + putPos(tag, "Base", this.basePos); + tag.putInt("PairTime", this.pairTime); + tag.putInt("PortalWidth", this.portalWidth); + tag.putInt("PortalHeight", this.portalHeight); + tag.putBoolean("MasterRole", this.master); + ListTag spots = new ListTag(); + for (BlockPos pos : this.portalSpots) { + spots.add(savePos(pos)); + } + tag.put("PortalSpots", spots); + ListTag compensated = new ListTag(); + for (BlockPos pos : this.compensatedSpots) { + compensated.add(savePos(pos)); + } + tag.put("CompensatedSpots", compensated); + return tag; + } + + public static PortalGunPortalFaceRecord load(CompoundTag tag) { + if (!tag.hasUUID("OwnerId") || !tag.hasUUID("PortalId")) { + return null; + } + ListTag spotsTag = tag.getList("PortalSpots", Tag.TAG_COMPOUND); + List spots = new java.util.ArrayList<>(spotsTag.size()); + for (int i = 0; i < spotsTag.size(); i++) { + spots.add(loadPos(spotsTag.getCompound(i))); + } + ListTag compensatedTag = tag.getList("CompensatedSpots", Tag.TAG_COMPOUND); + Set compensated = new HashSet<>(); + for (int i = 0; i < compensatedTag.size(); i++) { + compensated.add(loadPos(compensatedTag.getCompound(i))); + } + PortalGunPortalEntity.PortalSide side = tag.getInt("Side") == PortalGunPortalEntity.PortalSide.ORANGE.ordinal() + ? PortalGunPortalEntity.PortalSide.ORANGE + : PortalGunPortalEntity.PortalSide.BLUE; + int width = net.minecraft.util.Mth.clamp(tag.getInt("PortalWidth"), 1, 16); + int height = net.minecraft.util.Mth.clamp(tag.getInt("PortalHeight"), 2, 16); + return new PortalGunPortalFaceRecord( + tag.getUUID("OwnerId"), + tag.getString("OwnerIdentity"), + tag.hasUUID("GunId") ? tag.getUUID("GunId") : null, + tag.getString("ChannelName"), + tag.getUUID("PortalId"), + tag.hasUUID("LinkedPortalId") ? tag.getUUID("LinkedPortalId") : null, + side, + Direction.from3DDataValue(tag.getInt("Face")), + Direction.from3DDataValue(tag.getInt("UpAxis")), + getPos(tag, "Master"), + getPos(tag, "Base"), + spots, + compensated, + tag.getInt("PairTime"), + width, + height, + tag.getBoolean("MasterRole") + ); + } + + private static void putPos(CompoundTag tag, String key, BlockPos pos) { + CompoundTag value = new CompoundTag(); + value.putInt("X", pos.getX()); + value.putInt("Y", pos.getY()); + value.putInt("Z", pos.getZ()); + tag.put(key, value); + } + + private static BlockPos getPos(CompoundTag tag, String key) { + CompoundTag value = tag.getCompound(key); + return new BlockPos(value.getInt("X"), value.getInt("Y"), value.getInt("Z")); + } + + private static CompoundTag savePos(BlockPos pos) { + CompoundTag tag = new CompoundTag(); + tag.putInt("X", pos.getX()); + tag.putInt("Y", pos.getY()); + tag.putInt("Z", pos.getZ()); + return tag; + } + + private static BlockPos loadPos(CompoundTag tag) { + return new BlockPos(tag.getInt("X"), tag.getInt("Y"), tag.getInt("Z")); + } +} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/block/entity/PortalGunPortalMasterBlockEntity.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/block/entity/PortalGunPortalMasterBlockEntity.java index a0c1f10d..1b670bda 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/block/entity/PortalGunPortalMasterBlockEntity.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/block/entity/PortalGunPortalMasterBlockEntity.java @@ -5,8 +5,10 @@ import com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity; import com.craisinlord.antarchy.content.portalgun.PortalGunPlacement; import com.craisinlord.antarchy.content.portalgun.PortalGunSavedData; import java.util.ArrayList; +import java.util.EnumMap; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.UUID; import java.util.function.Supplier; @@ -30,14 +32,20 @@ import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.Vec3; -public class PortalGunPortalMasterBlockEntity extends BlockEntity { +public class PortalGunPortalMasterBlockEntity extends BlockEntity implements PortalGunPortalCellAccess { + private final Map faceRecords = new EnumMap<>(Direction.class); private UUID ownerId; + private String ownerIdentity; + private UUID gunId; + private String channelName; private UUID portalId; private PortalGunPortalEntity.PortalSide side = PortalGunPortalEntity.PortalSide.BLUE; private Direction facing = Direction.NORTH; private Direction upAxis = Direction.UP; private BlockPos basePos = BlockPos.ZERO; private BlockPos[] portalSpots = new BlockPos[] {BlockPos.ZERO, BlockPos.ZERO}; + private int portalWidth = 1; + private int portalHeight = 2; private Set compensatedSpots = Set.of(); private UUID linkedPortalId; private int pairTime; @@ -48,6 +56,9 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity { public void configure( UUID ownerId, + UUID gunId, + String ownerIdentity, + String channelName, UUID portalId, PortalGunPortalEntity.PortalSide side, Direction facing, @@ -55,20 +66,33 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity { BlockPos basePos, BlockPos[] portalSpots, Set compensatedSpots, - int pairTime + int pairTime, + int portalWidth, + int portalHeight ) { this.ownerId = ownerId; + this.ownerIdentity = ownerIdentity; + this.gunId = gunId; + this.channelName = channelName; this.portalId = portalId; this.side = side; this.facing = facing; this.upAxis = upAxis; this.basePos = basePos.immutable(); - this.portalSpots = new BlockPos[] {portalSpots[0].immutable(), portalSpots[1].immutable()}; + this.portalSpots = java.util.Arrays.stream(portalSpots).map(BlockPos::immutable).toArray(BlockPos[]::new); + this.portalWidth = portalWidth; + this.portalHeight = portalHeight; this.compensatedSpots = new HashSet<>(compensatedSpots); this.pairTime = pairTime; + this.portalGun$putFaceRecord(new PortalGunPortalFaceRecord(ownerId, ownerIdentity, gunId, channelName, portalId, this.linkedPortalId, side, facing, upAxis, this.worldPosition, this.basePos, List.of(this.portalSpots), this.compensatedSpots, pairTime, portalWidth, portalHeight, true)); this.setChanged(); } + @Override + public Map portalGun$getFaceRecords() { + return this.faceRecords; + } + public UUID getPortalId() { return this.portalId; } @@ -77,6 +101,36 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity { return this.ownerId; } + public UUID getGunId() { + return this.gunId; + } + + public String getChannelName() { + return this.channelName; + } + + public void adoptGunId(UUID gunId) { + if (this.gunId == null && gunId != null) { + this.gunId = gunId; + this.setChanged(); + } + } + + public void adoptChannelIdentity(UUID gunId, String channelName) { + boolean changed = false; + if (this.gunId == null && gunId != null) { + this.gunId = gunId; + changed = true; + } + if (channelName != null && !channelName.isEmpty() && !channelName.equals(this.channelName)) { + this.channelName = channelName; + changed = true; + } + if (changed) { + this.setChanged(); + } + } + public PortalGunPortalEntity.PortalSide getSide() { return this.side; } @@ -94,7 +148,16 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity { } public BlockPos[] getPortalSpots() { - return new BlockPos[] {this.portalSpots[0], this.portalSpots[1]}; + return this.portalSpots.clone(); + } + + public boolean containsPortalSpot(BlockPos pos) { + for (BlockPos portalSpot : this.portalSpots) { + if (portalSpot.equals(pos)) { + return true; + } + } + return false; } public Set getCompensatedSpots() { @@ -112,6 +175,10 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity { public void updatePair(UUID linkedPortalId, int pairTime) { this.linkedPortalId = linkedPortalId; this.pairTime = pairTime; + PortalGunPortalFaceRecord record = this.portalGun$getFaceRecord(this.facing, this.portalId); + if (record != null) { + this.portalGun$putFaceRecord(record.withPair(linkedPortalId, pairTime)); + } this.setChanged(); } @@ -120,11 +187,20 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity { } public void onBroken() { - if (!(this.level instanceof ServerLevel serverLevel) || this.portalId == null) { + if (!(this.level instanceof ServerLevel serverLevel)) { return; } - if (serverLevel.getEntity(this.portalId) instanceof PortalGunPortalEntity portal && !portal.isRemoved()) { - portal.discard(); + Set portalIds = new HashSet<>(); + if (this.portalId != null) { + portalIds.add(this.portalId); + } + for (PortalGunPortalFaceRecord record : this.faceRecords.values()) { + portalIds.add(record.portalId()); + } + for (UUID id : portalIds) { + if (serverLevel.getEntity(id) instanceof PortalGunPortalEntity portal && !portal.isRemoved()) { + portal.discard(); + } } } @@ -132,6 +208,12 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity { if (((level.getGameTime() + pos.asLong()) & 15L) != 0L) { return; } + blockEntity.portalGun$restoreMasterFaces(level, pos); + if (blockEntity.portalId != null + && !(level.getEntity(blockEntity.portalId) instanceof PortalGunPortalEntity) + && blockEntity.faceRecords.values().stream().anyMatch(record -> !record.portalId().equals(blockEntity.portalId))) { + return; + } if (!blockEntity.hasValidFootprint(level, pos)) { level.removeBlock(pos, false); return; @@ -146,6 +228,12 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity { level.removeBlock(pos, false); return; } + if (blockEntity.gunId == null && portal.getGunId() != null) { + blockEntity.adoptGunId(portal.getGunId()); + } else if (portal.getGunId() == null && blockEntity.gunId != null) { + portal.adoptGunId(blockEntity.gunId); + } + blockEntity.adoptChannelIdentity(portal.getGunId(), portal.getChannelName()); blockEntity.ensureLinkedPortal(level, portal); UUID linkedPortalId = portal.getLinkedPortalId(); if ((linkedPortalId == null && blockEntity.linkedPortalId != null) @@ -161,16 +249,24 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity { if (this.ownerId == null || this.portalId == null || !pos.equals(this.worldPosition)) { return false; } - if (!(level.getBlockEntity(this.basePos) instanceof PortalGunPortalBaseBlockEntity base) - || !base.matches(this.ownerId, this.portalId, this.side) - || !pos.equals(base.getMasterPos())) { + if (!PortalGunPlacement.isRectangularFootprint(this.portalSpots, this.facing, this.upAxis, this.portalWidth, this.portalHeight)) { return false; } - List supportPositions = new ArrayList<>(2); - for (BlockPos portalSpot : this.portalSpots) { + List supportPositions = new ArrayList<>(this.portalSpots.length); + for (int i = 0; i < this.portalSpots.length; i++) { + BlockPos portalSpot = this.portalSpots[i]; if (portalSpot == null || BlockPos.ZERO.equals(portalSpot)) { return false; } + if (i == 0) { + if (!portalSpot.equals(pos)) { + return false; + } + } else if (!(level.getBlockEntity(portalSpot) instanceof PortalGunPortalBaseBlockEntity base) + || !base.matches(this.ownerId, this.portalId, this.side) + || !pos.equals(base.getMasterPos())) { + return false; + } supportPositions.add(portalSpot.relative(this.facing.getOpposite())); } for (BlockPos supportPos : supportPositions) { @@ -186,23 +282,60 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity { if (this.portalId == null || this.ownerId == null) { return null; } + if (!PortalGunSavedData.isRegistered(level, this.ownerId, this.gunId, this.channelName, this.side, this.portalId)) { + return null; + } if (level.getEntity(this.portalId) instanceof PortalGunPortalEntity portal && !portal.isRemoved()) { return portal; } + PortalGunPortalFaceRecord record = this.portalGun$getFaceRecord(this.facing, this.portalId); + if (record != null) { + return restorePortalEntity(level, record); + } EntityType rawType = BuiltInRegistries.ENTITY_TYPE.get(ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "portal_gun_portal")); if (!(rawType instanceof EntityType)) { return null; } - PortalGunPlacement placement = PortalGunPlacement.fromStored(this.facing, this.upAxis, this.worldPosition, this.basePos, this.compensatedSpots); + PortalGunPlacement placement = PortalGunPlacement.fromStored(this.facing, this.upAxis, this.worldPosition, this.basePos, this.portalSpots, this.compensatedSpots, this.portalWidth, this.portalHeight); @SuppressWarnings("unchecked") PortalGunPortalEntity restored = new PortalGunPortalEntity((EntityType) rawType, level); restored.setUUID(this.portalId); - restored.configure(this.ownerId, this.side, placement); + restored.configure(this.ownerId, this.gunId, this.ownerIdentity == null ? String.valueOf(this.ownerId) : this.ownerIdentity, this.channelName == null ? String.valueOf(this.gunId) : this.channelName, this.side, placement); restored.restorePair(this.linkedPortalId, this.pairTime); Vec3 center = placement.center(); restored.moveTo(center.x, center.y, center.z, placement.yaw(), 0.0F); level.addFreshEntity(restored); - PortalGunSavedData.setPortal(level.getServer(), this.ownerId, this.side, restored.getUUID()); + PortalGunSavedData.setPortal(level, this.ownerId, this.gunId, this.channelName, this.side, restored.getUUID()); + return restored; + } + + public static PortalGunPortalEntity restorePortalEntity(ServerLevel level, PortalGunPortalFaceRecord record) { + if (record.ownerId() == null || record.portalId() == null + || !PortalGunSavedData.isRegistered(level, record.ownerId(), record.gunId(), record.channelName(), record.side(), record.portalId())) { + return null; + } + if (level.getEntity(record.portalId()) instanceof PortalGunPortalEntity portal && !portal.isRemoved()) { + return portal; + } + EntityType rawType = BuiltInRegistries.ENTITY_TYPE.get(ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "portal_gun_portal")); + if (rawType == null) { + return null; + } + PortalGunPlacement placement = record.placement(); + @SuppressWarnings("unchecked") + PortalGunPortalEntity restored = new PortalGunPortalEntity((EntityType) rawType, level); + restored.setUUID(record.portalId()); + restored.configure(record.ownerId(), record.gunId(), record.ownerIdentity(), record.channelName(), record.side(), placement); + restored.restorePair(record.linkedPortalId(), record.pairTime()); + Vec3 center = placement.center(); + restored.moveTo(center.x, center.y, center.z, placement.yaw(), 0.0F); + level.addFreshEntity(restored); + PortalGunSavedData.setPortal(level, record.ownerId(), record.gunId(), record.channelName(), record.side(), restored.getUUID()); + PortalGunPortalEntity linked = PortalGunSavedData.findLoadedPortal(level, record.ownerId(), record.gunId(), record.channelName(), record.side() == PortalGunPortalEntity.PortalSide.BLUE ? PortalGunPortalEntity.PortalSide.ORANGE : PortalGunPortalEntity.PortalSide.BLUE); + if (linked != null && linked != restored && !linked.isRemoved()) { + restored.linkTo(linked); + linked.linkTo(restored); + } return restored; } @@ -211,8 +344,10 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity { return; } PortalGunPortalEntity counterpart = PortalGunSavedData.findLoadedPortal( - level.getServer(), + level, this.ownerId, + this.gunId, + this.channelName, this.side == PortalGunPortalEntity.PortalSide.BLUE ? PortalGunPortalEntity.PortalSide.ORANGE : PortalGunPortalEntity.PortalSide.BLUE ); if (counterpart == null || counterpart == portal || counterpart.isRemoved()) { @@ -230,6 +365,15 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity { if (this.ownerId != null) { tag.putUUID("OwnerId", this.ownerId); } + if (this.ownerIdentity != null) { + tag.putString("OwnerIdentity", this.ownerIdentity); + } + if (this.gunId != null) { + tag.putUUID("GunId", this.gunId); + } + if (this.channelName != null) { + tag.putString("ChannelName", this.channelName); + } if (this.portalId != null) { tag.putUUID("PortalId", this.portalId); } @@ -243,6 +387,8 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity { tag.putInt("BaseY", this.basePos.getY()); tag.putInt("BaseZ", this.basePos.getZ()); tag.putInt("PairTime", this.pairTime); + tag.putInt("PortalWidth", this.portalWidth); + tag.putInt("PortalHeight", this.portalHeight); ListTag spots = new ListTag(); for (BlockPos portalSpot : this.portalSpots) { CompoundTag spot = new CompoundTag(); @@ -261,12 +407,16 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity { compensated.add(spot); } tag.put("CompensatedSpots", compensated); + this.portalGun$saveFaceRecords(tag); } @Override protected void loadAdditional(CompoundTag tag, HolderLookup.Provider registries) { super.loadAdditional(tag, registries); this.ownerId = tag.hasUUID("OwnerId") ? tag.getUUID("OwnerId") : null; + this.ownerIdentity = tag.contains("OwnerIdentity") ? tag.getString("OwnerIdentity") : String.valueOf(this.ownerId); + this.gunId = tag.hasUUID("GunId") ? tag.getUUID("GunId") : null; + this.channelName = tag.contains("ChannelName") ? tag.getString("ChannelName") : this.gunId == null ? "" : "Random Channel #" + this.gunId.hashCode(); this.portalId = tag.hasUUID("PortalId") ? tag.getUUID("PortalId") : null; this.linkedPortalId = tag.hasUUID("LinkedPortalId") ? tag.getUUID("LinkedPortalId") : null; this.side = tag.getInt("Side") == PortalGunPortalEntity.PortalSide.ORANGE.ordinal() ? PortalGunPortalEntity.PortalSide.ORANGE : PortalGunPortalEntity.PortalSide.BLUE; @@ -275,10 +425,15 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity { this.basePos = new BlockPos(tag.getInt("BaseX"), tag.getInt("BaseY"), tag.getInt("BaseZ")); this.pairTime = tag.getInt("PairTime"); ListTag spots = tag.getList("PortalSpots", Tag.TAG_COMPOUND); - for (int i = 0; i < Math.min(2, spots.size()); i++) { + if (!spots.isEmpty()) { + this.portalSpots = new BlockPos[spots.size()]; + } + for (int i = 0; i < spots.size(); i++) { CompoundTag spot = spots.getCompound(i); this.portalSpots[i] = new BlockPos(spot.getInt("X"), spot.getInt("Y"), spot.getInt("Z")); } + this.portalWidth = net.minecraft.util.Mth.clamp(tag.getInt("PortalWidth"), 1, 16); + this.portalHeight = net.minecraft.util.Mth.clamp(tag.getInt("PortalHeight"), 2, 16); ListTag compensated = tag.getList("CompensatedSpots", Tag.TAG_COMPOUND); Set compensatedSpots = new HashSet<>(); for (int i = 0; i < compensated.size(); i++) { @@ -286,6 +441,10 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity { compensatedSpots.add(new BlockPos(spot.getInt("X"), spot.getInt("Y"), spot.getInt("Z"))); } this.compensatedSpots = compensatedSpots; + this.portalGun$loadFaceRecords(tag); + if (this.faceRecords.isEmpty() && this.ownerId != null && this.portalId != null && this.portalSpots.length > 0) { + this.portalGun$putFaceRecord(new PortalGunPortalFaceRecord(this.ownerId, this.ownerIdentity, this.gunId, this.channelName, this.portalId, this.linkedPortalId, this.side, this.facing, this.upAxis, this.worldPosition, this.basePos, List.of(this.portalSpots), this.compensatedSpots, this.pairTime, this.portalWidth, this.portalHeight, true)); + } } @Override diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/block/entity/UpperBlockEntity.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/block/entity/UpperBlockEntity.java index 84671de3..bc30b922 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/block/entity/UpperBlockEntity.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/block/entity/UpperBlockEntity.java @@ -20,6 +20,7 @@ import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.inventory.AbstractContainerMenu; import net.minecraft.world.inventory.HopperMenu; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.ChestBlock; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.entity.RandomizableContainerBlockEntity; @@ -119,6 +120,10 @@ public class UpperBlockEntity extends RandomizableContainerBlockEntity { } private static Container getContainerAt(ServerLevel level, BlockPos pos) { + BlockState state = level.getBlockState(pos); + if (state.getBlock() instanceof ChestBlock chestBlock) { + return ChestBlock.getContainer(chestBlock, state, level, pos, false); + } BlockEntity blockEntity = level.getBlockEntity(pos); return blockEntity instanceof Container container ? container : null; } diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/PortalGunCrosshairRenderer.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/PortalGunCrosshairRenderer.java new file mode 100644 index 00000000..cf680add --- /dev/null +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/PortalGunCrosshairRenderer.java @@ -0,0 +1,116 @@ +package com.craisinlord.antarchy.content.client; + +import com.craisinlord.antarchy.Antarchy; +import com.craisinlord.antarchy.content.item.PortalGunItem; +import com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity; +import com.craisinlord.antarchy.content.client.PortalGunIndicatorClientState; +import com.craisinlord.antarchy.content.network.PortalGunIndicatorPayload; +import com.craisinlord.antarchy.config.AntarchySettings; +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.phys.AABB; + +public final class PortalGunCrosshairRenderer { + private static final ResourceLocation CROSSHAIR = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "textures/gui/portal_gun_crosshair.png"); + private static final int WIDTH = 34; + private static final int HEIGHT = 33; + private static ClientLevel cachedLevel; + private static long cachedGameTime = Long.MIN_VALUE; + private static UUID cachedOwnerId; + private static Set cachedPortals = Set.of(); + + private PortalGunCrosshairRenderer() { + } + + public static void render(GuiGraphics guiGraphics) { + Minecraft minecraft = Minecraft.getInstance(); + if (PortalGunGrabClientState.isActive()) { + return; + } + if (minecraft.player == null || minecraft.screen != null || minecraft.options.hideGui || !minecraft.options.getCameraType().isFirstPerson()) { + return; + } + ItemStack held = minecraft.player.getMainHandItem(); + if (!(held.getItem() instanceof PortalGunItem)) { + held = minecraft.player.getOffhandItem(); + if (!(held.getItem() instanceof PortalGunItem)) { + return; + } + } + UUID gunId = PortalGunItem.getGunId(held); + UUID ownerId = PortalGunItem.getPortalOwnerId(held, minecraft.player.getUUID()); + String channelName = PortalGunItem.getChannelName(held); + PortalGunIndicatorClientState.requestIfNeeded(minecraft, gunId); + Set portals = portalsFor(minecraft, ownerId, channelName); + PortalGunIndicatorPayload serverState = PortalGunIndicatorClientState.get(gunId); + boolean blueAvailable = serverState == null + ? portals.contains(new PortalKey(channelName, PortalGunPortalEntity.PortalSide.BLUE)) + : serverState.blueAvailable(); + boolean orangeAvailable = serverState == null + ? portals.contains(new PortalKey(channelName, PortalGunPortalEntity.PortalSide.ORANGE)) + : serverState.orangeAvailable(); + + int left = (minecraft.getWindow().getGuiScaledWidth() - WIDTH) / 2; + int top = (minecraft.getWindow().getGuiScaledHeight() - HEIGHT) / 2; + guiGraphics.blit(CROSSHAIR, left + 6, top, 0.0F, 0.0F, 17, HEIGHT, WIDTH, HEIGHT); + guiGraphics.blit(CROSSHAIR, left + 11, top, 17.0F, 0.0F, 17, HEIGHT, WIDTH, HEIGHT); + if (!blueAvailable) { + guiGraphics.fill(left + 6, top, left + 23, top + HEIGHT, 0x99000000); + } + if (!orangeAvailable) { + guiGraphics.fill(left + 11, top, left + 28, top + HEIGHT, 0x99000000); + } + int indicatorSize = AntarchySettings.portalGunIndicatorSize(); + if (indicatorSize > 0) { + int dot = Math.max(1, Math.round(HEIGHT * indicatorSize / 100.0F)); + int gap = Math.max(1, dot / 3); + int indicatorLeft = left + (WIDTH - dot * 2 - gap) / 2; + int indicatorTop = top + HEIGHT + 2; + guiGraphics.fill(indicatorLeft, indicatorTop, indicatorLeft + dot, indicatorTop + dot, + blueAvailable ? 0xFF21A8FF : 0xFF33414A); + int orangeLeft = indicatorLeft + dot + gap; + guiGraphics.fill(orangeLeft, indicatorTop, orangeLeft + dot, indicatorTop + dot, + orangeAvailable ? 0xFFFF8A24 : 0xFF4A4035); + } + } + + private static Set portalsFor(Minecraft minecraft, UUID ownerId, String channelName) { + ClientLevel level = minecraft.level; + long gameTime = level.getGameTime(); + if (cachedLevel != level || cachedGameTime != gameTime || !ownerId.equals(cachedOwnerId)) { + Set portals = new HashSet<>(); + if (minecraft.player != null) { + AABB bounds = minecraft.player.getBoundingBox().inflate(128.0D); + for (PortalGunPortalEntity portal : level.getEntitiesOfClass(PortalGunPortalEntity.class, bounds, Entity::isAlive)) { + UUID portalOwnerId = portal.getOwnerId(); + String portalChannelName = portal.getChannelName(); + if (ownerId.equals(portalOwnerId) && portalChannelName != null && !portalChannelName.isEmpty()) { + portals.add(new PortalKey(portalChannelName, portal.getPortalSide())); + if (portal.getLinkedPortalId() != null) { + portals.add(new PortalKey(portalChannelName, PortalGunPortalEntity.PortalSide.BLUE)); + portals.add(new PortalKey(portalChannelName, PortalGunPortalEntity.PortalSide.ORANGE)); + } + } + } + } + cachedLevel = level; + cachedGameTime = gameTime; + cachedOwnerId = ownerId; + cachedPortals = Set.copyOf(portals); + } + if (channelName == null || channelName.isEmpty()) { + return Set.of(); + } + return cachedPortals; + } + + private record PortalKey(String channelName, PortalGunPortalEntity.PortalSide side) { + } +} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/PortalGunEntityTransformationStack.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/PortalGunEntityTransformationStack.java index 2fc79ae4..552213f4 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/PortalGunEntityTransformationStack.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/PortalGunEntityTransformationStack.java @@ -27,30 +27,43 @@ public final class PortalGunEntityTransformationStack { } public Vec3 moveEntity(PortalGunPortalEntity sourcePortal, PortalGunPortalEntity destinationPortal, float partialTick) { - Vec3 interpolated = new Vec3( + double eyeHeight = this.entity.getEyeHeight(); + Vec3 interpolatedEye = new Vec3( net.minecraft.util.Mth.lerp(partialTick, this.entity.xo, this.entity.getX()), - net.minecraft.util.Mth.lerp(partialTick, this.entity.yo, this.entity.getY()), + net.minecraft.util.Mth.lerp(partialTick, this.entity.yo, this.entity.getY()) + eyeHeight, net.minecraft.util.Mth.lerp(partialTick, this.entity.zo, this.entity.getZ()) ); - Vec3 previous = new Vec3(this.entity.xo, this.entity.yo, this.entity.zo); - Vec3 transformed = destinationPortal.position().add(PortalGunTransformUtil.transformPosition(sourcePortal, destinationPortal, interpolated.subtract(sourcePortal.position()))); - Vec3 transformedPrevious = destinationPortal.position().add(PortalGunTransformUtil.transformPosition(sourcePortal, destinationPortal, previous.subtract(sourcePortal.position()))); + Vec3 previousEye = new Vec3(this.entity.xo, this.entity.yo + eyeHeight, this.entity.zo); + Vec3 transformedEye = destinationPortal.position().add(PortalGunTransformUtil.transformPosition(sourcePortal, destinationPortal, interpolatedEye.subtract(sourcePortal.position()))); + Vec3 transformedPreviousEye = destinationPortal.position().add(PortalGunTransformUtil.transformPosition(sourcePortal, destinationPortal, previousEye.subtract(sourcePortal.position()))); + Vec3 transformed = transformedEye.subtract(0.0D, eyeHeight, 0.0D); + Vec3 transformedPrevious = transformedPreviousEye.subtract(0.0D, eyeHeight, 0.0D); Vec3 look = PortalGunTransformUtil.transformVector(sourcePortal, destinationPortal, this.entity.getLookAngle()).normalize(); + Vec3 previousLook = PortalGunTransformUtil.transformVector( + sourcePortal, + destinationPortal, + Vec3.directionFromRotation(this.entity.xRotO, this.entity.yRotO) + ).normalize(); Vec3 velocity = PortalGunTransformUtil.transformVector(sourcePortal, destinationPortal, this.entity.getDeltaMovement()); float yaw = PortalGunTransformUtil.yawFromLook(look); float pitch = PortalGunTransformUtil.pitchFromLook(look); + float previousYaw = PortalGunTransformUtil.yawFromLook(previousLook); + float previousPitch = PortalGunTransformUtil.pitchFromLook(previousLook); + float yawDelta = net.minecraft.util.Mth.wrapDegrees(yaw - this.entity.getYRot()); this.entity.setPos(transformed.x, transformed.y, transformed.z); this.entity.xo = transformedPrevious.x; this.entity.yo = transformedPrevious.y; this.entity.zo = transformedPrevious.z; this.entity.setYRot(yaw); this.entity.setXRot(pitch); - this.entity.yRotO = yaw; - this.entity.xRotO = pitch; + this.entity.yRotO = previousYaw; + this.entity.xRotO = previousPitch; this.entity.setDeltaMovement(velocity); if (this.entity instanceof LivingEntity living) { - living.setYHeadRot(yaw); - living.setYBodyRot(yaw); + living.setYHeadRot(living.getYHeadRot() + yawDelta); + living.setYBodyRot(living.yBodyRot + yawDelta); + living.yHeadRotO += yawDelta; + living.yBodyRotO += yawDelta; } return transformed; } @@ -73,13 +86,17 @@ public final class PortalGunEntityTransformationStack { float yRotO, float xRotO, float yHeadRot, + float yHeadRotO, float yBodyRot, + float yBodyRotO, Vec3 deltaMovement ) { private static EntityTransformation capture(Entity entity) { float yHeadRot = entity instanceof LivingEntity living ? living.getYHeadRot() : entity.getYRot(); + float yHeadRotO = entity instanceof LivingEntity living ? living.yHeadRotO : entity.yRotO; float yBodyRot = entity instanceof LivingEntity living ? living.yBodyRot : entity.getYRot(); - return new EntityTransformation(entity.getX(), entity.getY(), entity.getZ(), entity.xo, entity.yo, entity.zo, entity.getYRot(), entity.getXRot(), entity.yRotO, entity.xRotO, yHeadRot, yBodyRot, entity.getDeltaMovement()); + float yBodyRotO = entity instanceof LivingEntity living ? living.yBodyRotO : entity.yRotO; + return new EntityTransformation(entity.getX(), entity.getY(), entity.getZ(), entity.xo, entity.yo, entity.zo, entity.getYRot(), entity.getXRot(), entity.yRotO, entity.xRotO, yHeadRot, yHeadRotO, yBodyRot, yBodyRotO, entity.getDeltaMovement()); } private void restore(Entity entity) { @@ -94,7 +111,9 @@ public final class PortalGunEntityTransformationStack { entity.setDeltaMovement(this.deltaMovement); if (entity instanceof LivingEntity living) { living.setYHeadRot(this.yHeadRot); + living.yHeadRotO = this.yHeadRotO; living.setYBodyRot(this.yBodyRot); + living.yBodyRotO = this.yBodyRotO; } } } diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/PortalGunGrabClientState.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/PortalGunGrabClientState.java new file mode 100644 index 00000000..f6779c39 --- /dev/null +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/PortalGunGrabClientState.java @@ -0,0 +1,20 @@ +package com.craisinlord.antarchy.content.client; + +public final class PortalGunGrabClientState { + private static boolean active; + + private PortalGunGrabClientState() { + } + + public static void update(boolean grabActive) { + active = grabActive; + } + + public static boolean isActive() { + return active; + } + + public static void clear() { + active = false; + } +} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/PortalGunIndicatorClientState.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/PortalGunIndicatorClientState.java new file mode 100644 index 00000000..03ca1c14 --- /dev/null +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/PortalGunIndicatorClientState.java @@ -0,0 +1,59 @@ +package com.craisinlord.antarchy.content.client; + +import com.craisinlord.antarchy.content.network.PortalGunIndicatorPayload; +import com.craisinlord.antarchy.content.network.PortalGunIndicatorRequestPayload; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; +import java.util.function.Consumer; +import net.minecraft.client.Minecraft; +import net.minecraft.client.multiplayer.ClientLevel; + +public final class PortalGunIndicatorClientState { + private static final Map STATES = new HashMap<>(); + private static ClientLevel level; + private static long lastRequestTick = Long.MIN_VALUE; + private static UUID lastRequestedGun; + private static Consumer requestSender = payload -> {}; + + private PortalGunIndicatorClientState() { + } + + public static void setRequestSender(Consumer sender) { + requestSender = sender; + } + + public static void requestIfNeeded(Minecraft minecraft, UUID gunId) { + ClientLevel currentLevel = minecraft.level; + if (currentLevel == null || gunId == null || minecraft.player == null) { + return; + } + if (level != currentLevel) { + level = currentLevel; + STATES.clear(); + lastRequestedGun = null; + lastRequestTick = Long.MIN_VALUE; + } + long currentTick = minecraft.player.tickCount; + if (!gunId.equals(lastRequestedGun) || currentTick - lastRequestTick >= 20L) { + lastRequestedGun = gunId; + lastRequestTick = currentTick; + requestSender.accept(new PortalGunIndicatorRequestPayload(gunId)); + } + } + + public static void update(PortalGunIndicatorPayload payload) { + STATES.put(payload.gunId(), payload); + } + + public static PortalGunIndicatorPayload get(UUID gunId) { + return gunId == null ? null : STATES.get(gunId); + } + + public static void clear() { + STATES.clear(); + level = null; + lastRequestedGun = null; + lastRequestTick = Long.MIN_VALUE; + } +} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/PortalGunPortalRenderState.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/PortalGunPortalRenderState.java index 6b3b3f94..ff5af61e 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/PortalGunPortalRenderState.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/PortalGunPortalRenderState.java @@ -3,6 +3,7 @@ package com.craisinlord.antarchy.content.client; import com.craisinlord.antarchy.content.portalgun.PortalGunWorldPortalShape; import java.util.ArrayDeque; import java.util.Deque; +import java.util.UUID; import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.Vec3; import org.joml.Matrix4f; @@ -133,7 +134,8 @@ public final class PortalGunPortalRenderState { Matrix4f viewMatrix, Matrix4f projectionMatrix, int renderLevel, - boolean renderAll + boolean renderAll, + UUID destinationId ) { } } diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/PortalGunResetClientState.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/PortalGunResetClientState.java new file mode 100644 index 00000000..03676a76 --- /dev/null +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/PortalGunResetClientState.java @@ -0,0 +1,51 @@ +package com.craisinlord.antarchy.content.client; + +import com.craisinlord.antarchy.content.network.PortalGunResetPayload; +import java.util.function.Consumer; +import net.minecraft.client.Minecraft; + +public final class PortalGunResetClientState { + private static boolean resetDown; + private static boolean attackDown; + private static boolean useDown; + private static boolean selectedSide; + + private PortalGunResetClientState() { + } + + public static boolean tick(Minecraft minecraft, boolean enabled, boolean resetPressed, Consumer sender) { + boolean canInput = enabled && minecraft.player != null && minecraft.level != null && minecraft.screen == null; + boolean active = canInput && resetPressed; + boolean attack = canInput && minecraft.options.keyAttack.isDown(); + boolean use = canInput && minecraft.options.keyUse.isDown(); + if (active && !resetDown) { + sender.accept(new PortalGunResetPayload(com.craisinlord.antarchy.content.portalgun.PortalGunResetManager.ARM)); + selectedSide = false; + } + if (active && attack && !attackDown) { + sender.accept(new PortalGunResetPayload(com.craisinlord.antarchy.content.portalgun.PortalGunResetManager.RESET_BLUE)); + selectedSide = true; + } + if (active && use && !useDown) { + selectedSide = true; + } + if (!active && resetDown) { + int action = !canInput || selectedSide + ? com.craisinlord.antarchy.content.portalgun.PortalGunResetManager.RELEASE + : com.craisinlord.antarchy.content.portalgun.PortalGunResetManager.RESET_ALL; + sender.accept(new PortalGunResetPayload(action)); + selectedSide = false; + } + resetDown = active; + attackDown = attack; + useDown = use; + return active; + } + + public static void clear() { + resetDown = false; + attackDown = false; + useDown = false; + selectedSide = false; + } +} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/PortalGunRollClientState.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/PortalGunRollClientState.java index 84e38223..7b4c4cdf 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/PortalGunRollClientState.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/PortalGunRollClientState.java @@ -7,10 +7,12 @@ import java.util.Map; import java.util.UUID; import net.minecraft.client.Minecraft; import net.minecraft.world.entity.Entity; +import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.Vec3; public final class PortalGunRollClientState { - private static final int TELEPORT_COOLDOWN_TICKS = 40; + private static final double PORTAL_QUERY_RADIUS = 12.0D; + private static final int TELEPORT_COOLDOWN_TICKS = 3; private static final float ROLL_DECAY = 0.85F; private static final float ROLL_EPSILON = 0.05F; private static final Map TELEPORT_COOLDOWNS = new HashMap<>(); @@ -32,8 +34,11 @@ public final class PortalGunRollClientState { if (minecraft.level == null || entity == null) { return false; } - for (Entity renderEntity : minecraft.level.entitiesForRendering()) { - if (renderEntity instanceof PortalGunPortalEntity portal && portal.isAlive() && portal.intersectsEntityBounds(entity)) { + for (PortalGunPortalEntity portal : minecraft.level.getEntitiesOfClass( + PortalGunPortalEntity.class, + entity.getBoundingBox().inflate(PORTAL_QUERY_RADIUS), + Entity::isAlive)) { + if (portal.intersectsEntityBounds(entity)) { return true; } } @@ -67,10 +72,10 @@ public final class PortalGunRollClientState { } long gameTime = minecraft.level.getGameTime(); TELEPORT_COOLDOWNS.entrySet().removeIf(entry -> entry.getValue() <= gameTime); - for (Entity entity : minecraft.level.entitiesForRendering()) { - if (!(entity instanceof PortalGunPortalEntity portal) || !portal.isAlive()) { - continue; - } + for (PortalGunPortalEntity portal : minecraft.level.getEntitiesOfClass( + PortalGunPortalEntity.class, + cameraEntity.getBoundingBox().inflate(PORTAL_QUERY_RADIUS), + Entity::isAlive)) { PortalGunPortalEntity linkedPortal = portal.getLinkedPortal(); if (linkedPortal == null || !linkedPortal.isAlive()) { continue; @@ -81,11 +86,13 @@ public final class PortalGunRollClientState { if (!canTeleport(cameraEntity, gameTime)) { continue; } - Vec3 crossingProbe = portal.resolveCrossingProbe(cameraEntity); - if (crossingProbe == null) { + AABB currentBox = cameraEntity.getBoundingBox(); + AABB previousBox = currentBox.move(cameraEntity.xo - cameraEntity.getX(), cameraEntity.yo - cameraEntity.getY(), cameraEntity.zo - cameraEntity.getZ()); + PortalGunPortalEntity.PortalCrossing crossing = portal.resolveCrossing(cameraEntity, previousBox, currentBox); + if (crossing == null) { continue; } - applyPredictedTeleport(cameraEntity, portal, linkedPortal, crossingProbe, gameTime); + applyPredictedTeleport(cameraEntity, portal, linkedPortal, crossing, gameTime); break; } } @@ -98,26 +105,30 @@ public final class PortalGunRollClientState { return cooldownUntil <= gameTime; } - private static void applyPredictedTeleport(Entity entity, PortalGunPortalEntity sourcePortal, PortalGunPortalEntity destinationPortal, Vec3 currentProbe, long gameTime) { + private static void applyPredictedTeleport(Entity entity, PortalGunPortalEntity sourcePortal, PortalGunPortalEntity destinationPortal, PortalGunPortalEntity.PortalCrossing crossing, long gameTime) { Vec3 transformedLook = PortalGunTransformUtil.transformVector(sourcePortal, destinationPortal, entity.getLookAngle()).normalize(); Vec3 transformedUp = PortalGunTransformUtil.transformVector(sourcePortal, destinationPortal, PortalGunTransformUtil.upVectorFromLookAndRoll(entity.getLookAngle(), rollDegrees)).normalize(); - Vec3 relativeProbe = currentProbe.subtract(sourcePortal.position()); - Vec3 transformedProbe = PortalGunTransformUtil.transformPosition(sourcePortal, destinationPortal, relativeProbe); - Vec3 transformedEntityOffset = PortalGunTransformUtil.transformVector(sourcePortal, destinationPortal, entity.position().subtract(currentProbe)); - Vec3 destinationNormal = destinationPortal.getNormalVec().normalize(); - double exitClearance = Math.max(0.35D, entity.getBbWidth() * 0.5D + 0.16D); - Vec3 exitProbe = destinationPortal.position().add(transformedProbe).add(destinationNormal.scale(exitClearance - transformedProbe.dot(destinationNormal))); - Vec3 exitPos = exitProbe.add(transformedEntityOffset); - Vec3 transformedMotion = PortalGunTransformUtil.transformVector(sourcePortal, destinationPortal, entity.getDeltaMovement()); - double outward = transformedMotion.dot(destinationNormal); - if (outward < 0.42D) { - transformedMotion = transformedMotion.add(destinationNormal.scale(0.42D - outward)); - } + Vec3 resolvedMovement = entity.position().subtract(entity.xo, entity.yo, entity.zo); + PortalGunPortalEntity.PortalTransitTransform transit = sourcePortal.previewTransit(entity, destinationPortal, crossing, resolvedMovement); + Vec3 transformedMotion = transit.movement(); + transformedMotion = clampPortalMotion(transformedMotion); rollDegrees = PortalGunTransformUtil.rollFromOrientation(transformedLook, transformedUp); + Vec3 exitPos = transit.exitPosition(); entity.teleportTo(exitPos.x, exitPos.y, exitPos.z); entity.setDeltaMovement(transformedMotion); + float verticalMotion = (float) transformedMotion.y; + entity.fallDistance = 0.1F * (verticalMotion / -0.1F * verticalMotion / -0.1F); entity.setYRot(PortalGunTransformUtil.yawFromLook(transformedLook)); entity.setXRot(PortalGunTransformUtil.pitchFromLook(transformedLook)); + entity.setPortalCooldown(TELEPORT_COOLDOWN_TICKS); TELEPORT_COOLDOWNS.put(entity.getUUID(), gameTime + TELEPORT_COOLDOWN_TICKS); } + + private static Vec3 clampPortalMotion(Vec3 motion) { + return new Vec3(clampPortalMotionComponent(motion.x), clampPortalMotionComponent(motion.y), clampPortalMotionComponent(motion.z)); + } + + private static double clampPortalMotionComponent(double motion) { + return Math.abs(motion) > 0.99D ? motion / (Math.abs(motion) + 0.001D) : motion; + } } diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/PortalGunZoomClientState.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/PortalGunZoomClientState.java new file mode 100644 index 00000000..faf14641 --- /dev/null +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/PortalGunZoomClientState.java @@ -0,0 +1,72 @@ +package com.craisinlord.antarchy.content.client; + +import com.craisinlord.antarchy.content.item.PortalGunItem; +import net.minecraft.client.Minecraft; +import net.minecraft.client.Options; +import net.minecraft.world.item.ItemStack; + +public final class PortalGunZoomClientState { + private static int progress; + private static boolean zooming; + private static Double originalSensitivity; + + private PortalGunZoomClientState() { + } + + public static void tick(Minecraft minecraft, boolean held, boolean togglePressed) { + if (minecraft.player == null || minecraft.level == null) { + zooming = false; + progress = 0; + restoreSensitivity(minecraft.options); + return; + } + + ItemStack main = minecraft.player.getMainHandItem(); + ItemStack off = minecraft.player.getOffhandItem(); + boolean holdingPortalGun = held && (main.getItem() instanceof PortalGunItem || off.getItem() instanceof PortalGunItem); + if (togglePressed && holdingPortalGun) { + zooming = !zooming; + if (zooming && originalSensitivity == null) { + originalSensitivity = minecraft.options.sensitivity().get(); + } + } + if (!holdingPortalGun) { + zooming = false; + } + + progress = Math.max(0, Math.min(5, progress + (zooming ? 1 : -1))); + if (originalSensitivity != null) { + if (progress == 0) { + restoreSensitivity(minecraft.options); + } + } + } + + public static double applyFov(double fov, float partialTick, Options options) { + if (progress == 0 && !zooming) { + restoreSensitivity(options); + return fov; + } + double easedProgress = zooming ? progress - 1.0D + partialTick : progress + 1.0D - partialTick; + double zoomFactor = zoomFactor(easedProgress); + if (originalSensitivity != null) { + options.sensitivity().set(originalSensitivity * zoomFactor); + } + return fov * zoomFactor; + } + + private static double zoomFactor(double progress) { + return 0.1D + 0.9D * (1.0D - Math.sin(Math.toRadians(90.0D * Math.max(0.0D, Math.min(1.0D, progress / 5.0D))))); + } + + private static double zoomFactor(int progress) { + return zoomFactor((double) progress); + } + + private static void restoreSensitivity(Options options) { + if (originalSensitivity != null) { + options.sensitivity().set(originalSensitivity); + originalSensitivity = null; + } + } +} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/game/AntarchyComputerGames.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/game/AntarchyComputerGames.java index d379407f..391ae0b0 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/game/AntarchyComputerGames.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/game/AntarchyComputerGames.java @@ -2,12 +2,8 @@ package com.craisinlord.antarchy.content.client.game; import com.craisinlord.antos.api.client.game.ComputerGame; import com.craisinlord.antos.api.client.game.ComputerGameRegistry; -import com.craisinlord.antarchy.content.network.AntarchyGameNetworking; -import com.craisinlord.antarchy.content.network.AntarchyGamePayload; -import com.craisinlord.antarchy.content.client.AntarchyGameClientState; import net.minecraft.client.gui.Font; import net.minecraft.client.gui.GuiGraphics; -import net.minecraft.core.BlockPos; import net.minecraft.resources.ResourceLocation; import java.util.function.IntPredicate; import java.util.function.Predicate; @@ -17,11 +13,11 @@ public final class AntarchyComputerGames { private AntarchyComputerGames() {} public static void register() { - registerIfMissing(game("basilisk", "BASILISK", pos -> { - BasiliskProgram program = new BasiliskProgram(true, 0, 0, score -> AntarchyGameNetworking.saveBasiliskScore(pos, score)); - var loaded = new java.util.concurrent.atomic.AtomicBoolean(); - AntarchyGameNetworking.requestBasiliskState(pos); - return new GameSession(() -> { if (!loaded.get()) loaded.set(loadScores(pos, AntarchyGamePayload.BASILISK_STATE, program::setStoredScores)); program.tick(); }, program::render, key -> loaded.get() && program.keyPressed(key), ignored -> false); + registerIfMissing(game("basilisk", "BASILISK", () -> { + // AntOS 1.2.1 creates game sessions without passing the computer position. + // Keep Basilisk playable without a position-bound score callback. + BasiliskProgram program = new BasiliskProgram(true, 0, 0, score -> {}); + return new GameSession(program::tick, program::render, program::keyPressed, ignored -> false); })); } @@ -29,18 +25,6 @@ public final class AntarchyComputerGames { if (ComputerGameRegistry.get(game.id()) == null) ComputerGameRegistry.register(game); } - private static boolean loadScores(BlockPos pos, int action, java.util.function.BiConsumer setter) { - String response = AntarchyGameClientState.getGameState(pos, action); - if (response == null) return false; - if (response.isBlank()) { AntarchyGameClientState.clearGameState(pos, action); return true; } - String[] scores = response.split("\0", 2); - if (scores.length != 2) return false; - try { setter.accept(Integer.parseInt(scores[0]), Integer.parseInt(scores[1])); } - catch (NumberFormatException ignored) { return false; } - AntarchyGameClientState.clearGameState(pos, action); - return true; - } - private static ComputerGame game(String path, String title, ProgramFactory factory) { ResourceLocation id = ResourceLocation.fromNamespaceAndPath("antarchy", path); ResourceLocation disk = ResourceLocation.fromNamespaceAndPath("antarchy", path + "_game"); @@ -48,12 +32,12 @@ public final class AntarchyComputerGames { @Override public ResourceLocation id() { return id; } @Override public ResourceLocation diskId() { return disk; } @Override public String title() { return title; } - @Override public Session create(BlockPos position) { return factory.create(position); } + @Override public Session create() { return factory.create(); } }; } @FunctionalInterface - private interface ProgramFactory { ComputerGame.Session create(BlockPos position); } + private interface ProgramFactory { ComputerGame.Session create(); } @FunctionalInterface private interface GameRenderer { void render(GuiGraphics graphics, Font font, int x, int y, int width, int height); } private static final class GameSession implements ComputerGame.Session { diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/PortalGunPortalRenderer.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/PortalGunPortalRenderer.java index 636e28cf..07603ae1 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/PortalGunPortalRenderer.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/PortalGunPortalRenderer.java @@ -1,11 +1,15 @@ package com.craisinlord.antarchy.content.client.renderer; +import com.craisinlord.antarchy.Antarchy; +import com.craisinlord.antarchy.config.AntarchySettings; import com.craisinlord.antarchy.content.client.model.PortalGunPortalModel; import com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity; import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.VertexConsumer; -import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.client.GraphicsStatus; +import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.LightTexture; +import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.entity.EntityRendererProvider; import net.minecraft.client.renderer.texture.OverlayTexture; @@ -24,23 +28,61 @@ public class PortalGunPortalRenderer extends GeoEntityRenderer RENDERERS = new LinkedHashMap<>(8, 0.75F, true); + private static final Set PROXY_RENDERERS = java.util.Collections.newSetFromMap(new IdentityHashMap<>()); + private static final ThreadLocal SECTION_BUDGET_DEADLINE = ThreadLocal.withInitial(() -> 0L); + private static final ThreadLocal SCHEDULED_SECTION_REBUILDS = ThreadLocal.withInitial(() -> 0); + private static final ThreadLocal SKIP_SET_NOT_DIRTY = ThreadLocal.withInitial(() -> false); + private static ClientLevel ownerLevel; + + private PortalGunPortalRendererPool() { + } + + public static LevelRenderer acquire(Minecraft minecraft, UUID destinationId, int width, int height) { + if (ownerLevel != minecraft.level) { + clear(); + ownerLevel = minecraft.level; + } + PortalRenderer state = RENDERERS.get(destinationId); + if (state == null) { + LevelRenderer renderer = new LevelRenderer( + minecraft, + minecraft.getEntityRenderDispatcher(), + minecraft.getBlockEntityRenderDispatcher(), + minecraft.renderBuffers() + ); + PROXY_RENDERERS.add(renderer); + renderer.setLevel(minecraft.level); + renderer.resize(width, height); + state = new PortalRenderer(renderer, width, height); + RENDERERS.put(destinationId, state); + trim(); + } else if (state.width() != width || state.height() != height) { + state.renderer().resize(width, height); + RENDERERS.put(destinationId, new PortalRenderer(state.renderer(), width, height)); + } + return state.renderer(); + } + + public static boolean isProxy(LevelRenderer renderer) { + return PROXY_RENDERERS.contains(renderer); + } + + public static void beginFrame() { + SECTION_BUDGET_DEADLINE.set(System.nanoTime() + PORTAL_SECTION_BUDGET_NANOS); + SCHEDULED_SECTION_REBUILDS.set(0); + SKIP_SET_NOT_DIRTY.set(false); + } + + public static void beginSceneBudget() { + SECTION_BUDGET_DEADLINE.set(System.nanoTime() + PORTAL_SECTION_BUDGET_NANOS); + SCHEDULED_SECTION_REBUILDS.set(0); + SKIP_SET_NOT_DIRTY.set(false); + } + + public static int scheduledSectionRebuilds() { + return SCHEDULED_SECTION_REBUILDS.get(); + } + + public static long remainingSectionBudgetNanos() { + return Math.max(0L, SECTION_BUDGET_DEADLINE.get() - System.nanoTime()); + } + + public static void rebuildSection(LevelRenderer renderer, SectionRenderDispatcher dispatcher, SectionRenderDispatcher.RenderSection section, RenderRegionCache cache) { + if (!isProxy(renderer)) { + dispatcher.rebuildSectionSync(section, cache); + SKIP_SET_NOT_DIRTY.set(false); + return; + } + if (remainingSectionBudgetNanos() <= 0L) { + SKIP_SET_NOT_DIRTY.set(true); + return; + } + section.rebuildSectionAsync(dispatcher, cache); + SCHEDULED_SECTION_REBUILDS.set(SCHEDULED_SECTION_REBUILDS.get() + 1); + SKIP_SET_NOT_DIRTY.set(false); + } + + public static void rebuildSectionAsync(LevelRenderer renderer, SectionRenderDispatcher dispatcher, SectionRenderDispatcher.RenderSection section, RenderRegionCache cache) { + if (!isProxy(renderer)) { + section.rebuildSectionAsync(dispatcher, cache); + SKIP_SET_NOT_DIRTY.set(false); + return; + } + if (remainingSectionBudgetNanos() <= 0L) { + SKIP_SET_NOT_DIRTY.set(true); + return; + } + section.rebuildSectionAsync(dispatcher, cache); + SCHEDULED_SECTION_REBUILDS.set(SCHEDULED_SECTION_REBUILDS.get() + 1); + SKIP_SET_NOT_DIRTY.set(false); + } + + public static void setNotDirty(SectionRenderDispatcher.RenderSection section) { + boolean leaveDirty = SKIP_SET_NOT_DIRTY.get(); + SKIP_SET_NOT_DIRTY.set(false); + if (!leaveDirty) { + section.setNotDirty(); + } + } + + public static void clear() { + for (PortalRenderer state : new ArrayList<>(RENDERERS.values())) { + LevelRenderer renderer = state.renderer(); + PortalGunPortalViewAreaManager.releaseRenderer(renderer); + try { + renderer.close(); + } finally { + PROXY_RENDERERS.remove(renderer); + } + } + RENDERERS.clear(); + ownerLevel = null; + } + + private static void trim() { + Iterator> iterator = RENDERERS.entrySet().iterator(); + while (RENDERERS.size() > MAX_RENDERERS && iterator.hasNext()) { + PortalRenderer state = iterator.next().getValue(); + iterator.remove(); + LevelRenderer renderer = state.renderer(); + PortalGunPortalViewAreaManager.releaseRenderer(renderer); + try { + renderer.close(); + } finally { + PROXY_RENDERERS.remove(renderer); + } + } + } + + private record PortalRenderer(LevelRenderer renderer, int width, int height) { + } +} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/PortalGunPortalViewAreaManager.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/PortalGunPortalViewAreaManager.java new file mode 100644 index 00000000..4c3d6a06 --- /dev/null +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/PortalGunPortalViewAreaManager.java @@ -0,0 +1,403 @@ +package com.craisinlord.antarchy.content.client.renderer; + +import com.craisinlord.antarchy.mixins.client.LevelRendererPortalViewAreaAccessor; +import com.mojang.blaze3d.vertex.VertexBuffer; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import java.util.Collections; +import java.util.IdentityHashMap; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import net.minecraft.client.CloudStatus; +import net.minecraft.client.Minecraft; +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.client.renderer.LevelRenderer; +import net.minecraft.client.renderer.SectionOcclusionGraph; +import net.minecraft.client.renderer.ViewArea; +import net.minecraft.client.renderer.blockentity.BlockEntityRenderDispatcher; +import net.minecraft.client.renderer.chunk.SectionRenderDispatcher; +import net.minecraft.core.SectionPos; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.phys.HitResult; +import net.minecraft.world.phys.Vec3; + +public final class PortalGunPortalViewAreaManager { + private static final int MAX_VIEW_AREAS = 8; + private static final LinkedHashMap VIEW_AREAS = new LinkedHashMap<>(8, 0.75F, true); + private static final Map SECTION_OWNERS = Collections.synchronizedMap(new IdentityHashMap<>()); + private static ClientLevel ownerLevel; + private static int ownerViewDistance = -1; + + private PortalGunPortalViewAreaManager() { + } + + public static Scope enter(Minecraft minecraft, LevelRenderer renderer, UUID destinationId, Vec3 cameraPos, net.minecraft.client.Camera portalCamera) { + LevelRendererPortalViewAreaAccessor accessor = (LevelRendererPortalViewAreaAccessor) renderer; + int viewDistance = minecraft.options.getEffectiveRenderDistance(); + if (ownerLevel != minecraft.level || ownerViewDistance != viewDistance) { + clear(); + ownerLevel = minecraft.level; + ownerViewDistance = viewDistance; + } + Scope sodiumScope = SodiumCompat.isLoaded() ? new SodiumScope(SodiumCompat.beginPortalView(portalCamera)) : null; + ViewKey viewKey = new ViewKey(renderer, destinationId); + ViewState state = VIEW_AREAS.get(viewKey); + if (state == null) { + ViewArea area = accessor.antarchy$getViewArea(); + SectionOcclusionGraph graph = accessor.antarchy$getSectionOcclusionGraph(); + ObjectArrayList sections = accessor.antarchy$getVisibleSections(); + state = new ViewState(area, graph, sections, accessor.antarchy$getGlobalBlockEntities()); + state.cloudBuffer = accessor.antarchy$getCloudBuffer(); + state.generateClouds = accessor.antarchy$getGenerateClouds(); + state.prevCloudX = accessor.antarchy$getPrevCloudX(); + state.prevCloudY = accessor.antarchy$getPrevCloudY(); + state.prevCloudZ = accessor.antarchy$getPrevCloudZ(); + state.prevCloudColor = accessor.antarchy$getPrevCloudColor(); + state.prevCloudsType = accessor.antarchy$getPrevCloudsType(); + for (SectionRenderDispatcher.RenderSection section : area.sections) { + ((PortalViewSectionMarker) section).antarchy$markPortalViewSection(); + SECTION_OWNERS.put(section, state); + } + VIEW_AREAS.put(viewKey, state); + trimOldViews(); + } + state.area.repositionCamera(cameraPos.x, cameraPos.z); + int sectionX = SectionPos.posToSectionCoord(cameraPos.x); + int sectionY = SectionPos.posToSectionCoord(cameraPos.y); + int sectionZ = SectionPos.posToSectionCoord(cameraPos.z); + state.cameraPos = cameraPos; + state.lastSectionX = sectionX; + state.lastSectionY = sectionY; + state.lastSectionZ = sectionZ; + Scope scope = new VanillaScope(renderer, accessor, state, minecraft.getBlockEntityRenderDispatcher()); + accessor.antarchy$setViewArea(state.area); + accessor.antarchy$setSectionOcclusionGraph(state.graph); + accessor.antarchy$setVisibleSections(state.visibleSections); + accessor.antarchy$setGlobalBlockEntities(state.globalBlockEntities); + accessor.antarchy$setLastCameraSectionX(sectionX); + accessor.antarchy$setLastCameraSectionY(sectionY); + accessor.antarchy$setLastCameraSectionZ(sectionZ); + return sodiumScope == null ? scope : new CombinedScope(scope, sodiumScope); + } + + public static boolean routeSectionCompiled(SectionRenderDispatcher.RenderSection section) { + ViewState state = SECTION_OWNERS.get(section); + if (state == null) { + return ((PortalViewSectionMarker) section).antarchy$isPortalViewSection(); + } + state.graph.onSectionCompiled(section); + return true; + } + + public static Vec3 cameraForSection(SectionRenderDispatcher.RenderSection section, Vec3 fallback) { + ViewState state = SECTION_OWNERS.get(section); + return state == null || state.cameraPos == null ? fallback : state.cameraPos; + } + + public static boolean updateGlobalBlockEntities(SectionRenderDispatcher.RenderSection section, Iterable removed, Iterable added) { + ViewState state = SECTION_OWNERS.get(section); + if (state == null) { + return ((PortalViewSectionMarker) section).antarchy$isPortalViewSection(); + } + synchronized (state.globalBlockEntities) { + for (BlockEntity blockEntity : removed) { + state.globalBlockEntities.remove(blockEntity); + } + for (BlockEntity blockEntity : added) { + state.globalBlockEntities.add(blockEntity); + } + } + return true; + } + + public static void onSectionDirty(int sectionX, int sectionY, int sectionZ, boolean playerChanged) { + for (ViewState state : VIEW_AREAS.values()) { + int sectionRange = state.area.getViewDistance() + 1; + if (Math.abs((long) sectionX - state.lastSectionX) > sectionRange + || Math.abs((long) sectionZ - state.lastSectionZ) > sectionRange + || sectionY < ownerLevel.getMinSection() + || sectionY > ownerLevel.getMaxSection()) { + continue; + } + state.area.setDirty(sectionX, sectionY, sectionZ, playerChanged); + } + } + + public static void onChunkLoaded(net.minecraft.world.level.ChunkPos pos) { + for (ViewState state : VIEW_AREAS.values()) { + state.graph.onChunkLoaded(pos); + markChunkDirty(state, pos); + } + } + + public static void onChunkDropped(net.minecraft.world.level.ChunkPos pos) { + for (ViewState state : VIEW_AREAS.values()) { + markChunkDirty(state, pos); + state.graph.invalidate(); + } + } + + private static void markChunkDirty(ViewState state, net.minecraft.world.level.ChunkPos pos) { + int sectionRange = state.area.getViewDistance() + 1; + if (Math.abs((long) pos.x - state.lastSectionX) <= sectionRange + && Math.abs((long) pos.z - state.lastSectionZ) <= sectionRange) { + for (int sectionY = ownerLevel.getMinSection(); sectionY <= ownerLevel.getMaxSection(); sectionY++) { + state.area.setDirty(pos.x, sectionY, pos.z, true); + } + } + } + + public static void clear() { + SodiumCompat.clear(); + for (ViewState state : VIEW_AREAS.values()) { + state.graph.waitAndReset(state.area); + } + VIEW_AREAS.clear(); + SECTION_OWNERS.clear(); + ownerLevel = null; + ownerViewDistance = -1; + } + + public static void releaseRenderer(LevelRenderer renderer) { + Iterator> iterator = VIEW_AREAS.entrySet().iterator(); + while (iterator.hasNext()) { + Map.Entry entry = iterator.next(); + if (entry.getKey().renderer() == renderer) { + ViewState state = entry.getValue(); + state.graph.waitAndReset(state.area); + removeOwners(state); + iterator.remove(); + } + } + } + + private static void trimOldViews() { + Iterator> iterator = VIEW_AREAS.entrySet().iterator(); + while (VIEW_AREAS.size() > MAX_VIEW_AREAS && iterator.hasNext()) { + ViewState state = iterator.next().getValue(); + iterator.remove(); + state.graph.waitAndReset(state.area); + removeOwners(state); + } + } + + private static void removeOwners(ViewState state) { + for (SectionRenderDispatcher.RenderSection section : state.area.sections) { + SECTION_OWNERS.remove(section); + } + } + + private static final class ViewState { + private final ViewArea area; + private final SectionOcclusionGraph graph; + private final ObjectArrayList visibleSections; + private final Set globalBlockEntities; + private VertexBuffer cloudBuffer; + private boolean generateClouds = true; + private int prevCloudX = Integer.MIN_VALUE; + private int prevCloudY = Integer.MIN_VALUE; + private int prevCloudZ = Integer.MIN_VALUE; + private Vec3 prevCloudColor = Vec3.ZERO; + private CloudStatus prevCloudsType; + private int lastSectionX; + private int lastSectionY; + private int lastSectionZ; + private volatile Vec3 cameraPos; + private double transparentSortX = Double.NaN; + private double transparentSortY = Double.NaN; + private double transparentSortZ = Double.NaN; + private double prevCamX = Double.NaN; + private double prevCamY = Double.NaN; + private double prevCamZ = Double.NaN; + private double prevCamRotX = Double.NaN; + private double prevCamRotY = Double.NaN; + + private ViewState(ViewArea area, SectionOcclusionGraph graph, ObjectArrayList visibleSections, Set globalBlockEntities) { + this.area = area; + this.graph = graph; + this.visibleSections = visibleSections; + this.globalBlockEntities = globalBlockEntities; + } + } + + private record ViewKey(LevelRenderer renderer, UUID destinationId) { + } + + public interface Scope extends AutoCloseable { + @Override + void close(); + } + + private static final class CombinedScope implements Scope { + private final Scope viewAreaScope; + private final Scope sodiumScope; + + private CombinedScope(Scope viewAreaScope, Scope sodiumScope) { + this.viewAreaScope = viewAreaScope; + this.sodiumScope = sodiumScope; + } + + @Override + public void close() { + try { + viewAreaScope.close(); + } finally { + sodiumScope.close(); + } + } + } + + private static final class SodiumScope implements Scope { + private final SodiumCompat.TerrainScope terrainScope; + + private SodiumScope(SodiumCompat.TerrainScope terrainScope) { + this.terrainScope = terrainScope; + } + + @Override + public void close() { + terrainScope.close(); + } + } + + private static final class VanillaScope implements Scope { + private final LevelRendererPortalViewAreaAccessor accessor; + private final ViewArea previousArea; + private final SectionOcclusionGraph previousGraph; + private final ObjectArrayList previousVisibleSections; + private final Set previousGlobalBlockEntities; + private final VertexBuffer previousCloudBuffer; + private final boolean previousGenerateClouds; + private final int previousCloudX; + private final int previousCloudY; + private final int previousCloudZ; + private final Vec3 previousCloudColor; + private final CloudStatus previousCloudsType; + private final int previousRainSoundTime; + private final int previousSectionX; + private final int previousSectionY; + private final int previousSectionZ; + private final double previousPrevCamX; + private final double previousPrevCamY; + private final double previousPrevCamZ; + private final double previousPrevCamRotX; + private final double previousPrevCamRotY; + private final double previousTransparentSortX; + private final double previousTransparentSortY; + private final double previousTransparentSortZ; + private final Vec3 previousDispatcherCamera; + private final SectionRenderDispatcher dispatcher; + private final BlockEntityRenderDispatcher blockEntityDispatcher; + private final Level previousBlockEntityLevel; + private final net.minecraft.client.Camera previousBlockEntityCamera; + private final HitResult previousBlockEntityHitResult; + private final ViewState state; + private boolean closed; + + private VanillaScope(LevelRenderer renderer, LevelRendererPortalViewAreaAccessor accessor, ViewState state, BlockEntityRenderDispatcher blockEntityDispatcher) { + this.accessor = accessor; + this.state = state; + this.blockEntityDispatcher = blockEntityDispatcher; + this.previousBlockEntityLevel = blockEntityDispatcher.level; + this.previousBlockEntityCamera = blockEntityDispatcher.camera; + this.previousBlockEntityHitResult = blockEntityDispatcher.cameraHitResult; + this.previousArea = accessor.antarchy$getViewArea(); + this.previousGraph = accessor.antarchy$getSectionOcclusionGraph(); + this.previousVisibleSections = accessor.antarchy$getVisibleSections(); + this.previousGlobalBlockEntities = accessor.antarchy$getGlobalBlockEntities(); + this.previousCloudBuffer = accessor.antarchy$getCloudBuffer(); + this.previousGenerateClouds = accessor.antarchy$getGenerateClouds(); + this.previousCloudX = accessor.antarchy$getPrevCloudX(); + this.previousCloudY = accessor.antarchy$getPrevCloudY(); + this.previousCloudZ = accessor.antarchy$getPrevCloudZ(); + this.previousCloudColor = accessor.antarchy$getPrevCloudColor(); + this.previousCloudsType = accessor.antarchy$getPrevCloudsType(); + this.previousRainSoundTime = accessor.antarchy$getRainSoundTime(); + this.previousSectionX = accessor.antarchy$getLastCameraSectionX(); + this.previousSectionY = accessor.antarchy$getLastCameraSectionY(); + this.previousSectionZ = accessor.antarchy$getLastCameraSectionZ(); + this.previousPrevCamX = accessor.antarchy$getPrevCamX(); + this.previousPrevCamY = accessor.antarchy$getPrevCamY(); + this.previousPrevCamZ = accessor.antarchy$getPrevCamZ(); + this.previousPrevCamRotX = accessor.antarchy$getPrevCamRotX(); + this.previousPrevCamRotY = accessor.antarchy$getPrevCamRotY(); + this.previousTransparentSortX = accessor.antarchy$getTransparentSortX(); + this.previousTransparentSortY = accessor.antarchy$getTransparentSortY(); + this.previousTransparentSortZ = accessor.antarchy$getTransparentSortZ(); + this.dispatcher = renderer.getSectionRenderDispatcher(); + this.previousDispatcherCamera = dispatcher.getCameraPosition(); + accessor.antarchy$setLastCameraSectionX(state.lastSectionX); + accessor.antarchy$setLastCameraSectionY(state.lastSectionY); + accessor.antarchy$setLastCameraSectionZ(state.lastSectionZ); + accessor.antarchy$setPrevCamX(state.prevCamX); + accessor.antarchy$setPrevCamY(state.prevCamY); + accessor.antarchy$setPrevCamZ(state.prevCamZ); + accessor.antarchy$setPrevCamRotX(state.prevCamRotX); + accessor.antarchy$setPrevCamRotY(state.prevCamRotY); + accessor.antarchy$setTransparentSortX(state.transparentSortX); + accessor.antarchy$setTransparentSortY(state.transparentSortY); + accessor.antarchy$setTransparentSortZ(state.transparentSortZ); + accessor.antarchy$setCloudBuffer(state.cloudBuffer); + accessor.antarchy$setGenerateClouds(state.generateClouds); + accessor.antarchy$setPrevCloudX(state.prevCloudX); + accessor.antarchy$setPrevCloudY(state.prevCloudY); + accessor.antarchy$setPrevCloudZ(state.prevCloudZ); + accessor.antarchy$setPrevCloudColor(state.prevCloudColor); + accessor.antarchy$setPrevCloudsType(state.prevCloudsType); + } + + @Override + public void close() { + if (closed) { + return; + } + closed = true; + state.lastSectionX = accessor.antarchy$getLastCameraSectionX(); + state.lastSectionY = accessor.antarchy$getLastCameraSectionY(); + state.lastSectionZ = accessor.antarchy$getLastCameraSectionZ(); + state.prevCamX = accessor.antarchy$getPrevCamX(); + state.prevCamY = accessor.antarchy$getPrevCamY(); + state.prevCamZ = accessor.antarchy$getPrevCamZ(); + state.prevCamRotX = accessor.antarchy$getPrevCamRotX(); + state.prevCamRotY = accessor.antarchy$getPrevCamRotY(); + state.transparentSortX = accessor.antarchy$getTransparentSortX(); + state.transparentSortY = accessor.antarchy$getTransparentSortY(); + state.transparentSortZ = accessor.antarchy$getTransparentSortZ(); + state.cloudBuffer = accessor.antarchy$getCloudBuffer(); + state.generateClouds = accessor.antarchy$getGenerateClouds(); + state.prevCloudX = accessor.antarchy$getPrevCloudX(); + state.prevCloudY = accessor.antarchy$getPrevCloudY(); + state.prevCloudZ = accessor.antarchy$getPrevCloudZ(); + state.prevCloudColor = accessor.antarchy$getPrevCloudColor(); + state.prevCloudsType = accessor.antarchy$getPrevCloudsType(); + accessor.antarchy$setViewArea(previousArea); + accessor.antarchy$setSectionOcclusionGraph(previousGraph); + accessor.antarchy$setVisibleSections(previousVisibleSections); + accessor.antarchy$setGlobalBlockEntities(previousGlobalBlockEntities); + accessor.antarchy$setCloudBuffer(previousCloudBuffer); + accessor.antarchy$setGenerateClouds(previousGenerateClouds); + accessor.antarchy$setPrevCloudX(previousCloudX); + accessor.antarchy$setPrevCloudY(previousCloudY); + accessor.antarchy$setPrevCloudZ(previousCloudZ); + accessor.antarchy$setPrevCloudColor(previousCloudColor); + accessor.antarchy$setPrevCloudsType(previousCloudsType); + accessor.antarchy$setRainSoundTime(previousRainSoundTime); + accessor.antarchy$setLastCameraSectionX(previousSectionX); + accessor.antarchy$setLastCameraSectionY(previousSectionY); + accessor.antarchy$setLastCameraSectionZ(previousSectionZ); + accessor.antarchy$setPrevCamX(previousPrevCamX); + accessor.antarchy$setPrevCamY(previousPrevCamY); + accessor.antarchy$setPrevCamZ(previousPrevCamZ); + accessor.antarchy$setPrevCamRotX(previousPrevCamRotX); + accessor.antarchy$setPrevCamRotY(previousPrevCamRotY); + accessor.antarchy$setTransparentSortX(previousTransparentSortX); + accessor.antarchy$setTransparentSortY(previousTransparentSortY); + accessor.antarchy$setTransparentSortZ(previousTransparentSortZ); + dispatcher.setCamera(previousDispatcherCamera); + blockEntityDispatcher.prepare(previousBlockEntityLevel, previousBlockEntityCamera, previousBlockEntityHitResult); + } + } +} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/PortalGunPortalViewRenderer.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/PortalGunPortalViewRenderer.java index e5155afe..ddc62d33 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/PortalGunPortalViewRenderer.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/PortalGunPortalViewRenderer.java @@ -1,12 +1,18 @@ package com.craisinlord.antarchy.content.client.renderer; import com.craisinlord.antarchy.Antarchy; +import com.craisinlord.antarchy.config.AntarchySettings; import com.craisinlord.antarchy.content.client.PortalGunEntityTransformationStack; import com.craisinlord.antarchy.content.client.PortalGunPortalRenderState; +import com.craisinlord.antarchy.mixins.client.MinecraftMainRenderTargetAccessor; +import com.craisinlord.antarchy.mixins.client.LevelRendererPortalTargetsAccessor; +import com.craisinlord.antarchy.mixins.client.PostChainTargetAccessor; import com.craisinlord.antarchy.content.portalgun.PortalGunTransformUtil; import com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity; import com.craisinlord.antarchy.content.portalgun.PortalGunWorldPortalShape; +import com.craisinlord.antarchy.mixins.client.CameraBasisAccessor; import com.mojang.blaze3d.platform.GlStateManager; +import com.mojang.blaze3d.pipeline.RenderTarget; import com.mojang.blaze3d.pipeline.TextureTarget; import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.vertex.BufferBuilder; @@ -17,218 +23,443 @@ import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.Tesselator; import com.mojang.blaze3d.vertex.VertexFormat; import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.IdentityHashMap; import java.util.List; +import java.util.Set; import net.minecraft.client.Camera; import net.minecraft.client.DeltaTracker; +import net.minecraft.client.GraphicsStatus; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.client.renderer.GameRenderer; +import net.minecraft.client.renderer.LevelRenderer; +import net.minecraft.client.renderer.PostChain; +import net.minecraft.client.renderer.ShaderInstance; import net.minecraft.resources.ResourceLocation; import net.minecraft.util.Mth; import net.minecraft.world.entity.Entity; +import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.Vec3; import org.joml.Matrix4f; import org.joml.Vector4f; import org.joml.Quaternionf; public final class PortalGunPortalViewRenderer { - private static final int MAX_RECURSION_DEPTH = 3; - private static final double SURFACE_OFFSET = 0.03125D; - private static final double CAMERA_OFFSET = 0.0625D; + private static final int MAX_RECURSION_DEPTH = 1; + private static final int MAX_PORTAL_VIEWS_PER_FRAME = 2; + private static final double RENDER_TARGET_SCALE = 1.0D; + private static final boolean ENABLE_DESTINATION_SCENE_RENDERING = true; + private static final int PORTAL_TARGET_FILTER = 9729; + private static final double PORTAL_APERTURE_SCALE = 1.0D; + private static final double SURFACE_OFFSET = 0.04D; private static final double OVERLAY_OFFSET = 0.03135D; private static final ResourceLocation BLUE_OVERLAY = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "textures/vfx/portal_gun_portal_blue.png"); private static final ResourceLocation ORANGE_OVERLAY = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "textures/vfx/portal_gun_portal_orange.png"); private static final TextureTarget[] PORTAL_TARGETS = new TextureTarget[MAX_RECURSION_DEPTH + 1]; + private static TextureTarget depthBackup; + private static ShaderInstance portalViewShader; private static boolean renderingPortalView; - private static long lastRenderLogTick = Long.MIN_VALUE; - private static long lastHookLogTick = Long.MIN_VALUE; + private static boolean loggedRendererActivation; + private static String lastPortalCandidateState = ""; + private static final Set loggedPortalSceneRenders = new HashSet<>(); + private static final Set loggedPortalViewComposites = new HashSet<>(); + private static boolean loggedMissingPortalViewShader; + private static final TargetAllocationFailure[] TARGET_ALLOCATION_FAILURES = new TargetAllocationFailure[MAX_RECURSION_DEPTH + 1]; + private static long lastRenderFailureTick = Long.MIN_VALUE; private PortalGunPortalViewRenderer() { } - public static boolean isEnabled() { - return false; + public static void setPortalViewShader(ShaderInstance shader) { + portalViewShader = shader; + Antarchy.LOGGER.info("Portal gun view shader registered"); } - public static void render(Camera camera, Matrix4f poseMatrix, DeltaTracker tickCounter) { + public static void clearPortalViewShader() { + portalViewShader = null; + } + + public static boolean isEnabled() { + return ENABLE_DESTINATION_SCENE_RENDERING && AntarchySettings.portalGunSeeThroughPortals(); + } + + public static void releaseTargets() { + PortalSceneRenderTrace.reset(); + for (int i = 0; i < PORTAL_TARGETS.length; i++) { + TextureTarget target = PORTAL_TARGETS[i]; + if (target != null) { + PortalStencilTarget.release(target); + target.destroyBuffers(); + PORTAL_TARGETS[i] = null; + } + } + java.util.Arrays.fill(TARGET_ALLOCATION_FAILURES, null); + if (depthBackup != null) { + depthBackup.destroyBuffers(); + depthBackup = null; + } + } + + public static void render(Camera camera, Matrix4f poseMatrix, Matrix4f projectionMatrix, DeltaTracker tickCounter) { if (!isEnabled()) { return; } if (renderingPortalView) { return; } + PortalGunPortalRendererPool.beginFrame(); Minecraft minecraft = Minecraft.getInstance(); - if (minecraft.level == null || minecraft.gameRenderer == null || camera == null || poseMatrix == null || tickCounter == null) { - logRenderUnavailable(minecraft, camera, poseMatrix, tickCounter); + if (minecraft.level == null || minecraft.gameRenderer == null || camera == null || poseMatrix == null || projectionMatrix == null || tickCounter == null) { return; } - logRenderHook(minecraft, camera); + if (!loggedRendererActivation) { + loggedRendererActivation = true; + Antarchy.LOGGER.info("Portal gun view renderer hook active"); + } Entity cameraEntity = minecraft.getCameraEntity(); if (cameraEntity == null) { return; } - List portals = collectVisiblePortals(minecraft, camera); - if (portals.isEmpty()) { - logRenderEmpty(minecraft, camera.getPosition()); - return; - } - ensureTargets(minecraft); - if (PORTAL_TARGETS[0] == null) { - return; - } Vec3 cameraPos = camera.getPosition(); Vec3 rootLook = new Vec3(camera.getLookVector()).normalize(); Vec3 rootUp = new Vec3(camera.getUpVector()).normalize(); Matrix4f rootViewMatrix = new Matrix4f().rotation(PortalGunTransformUtil.orientationQuaternion(rootLook, rootUp).conjugate(new Quaternionf())); - RenderContext rootContext = new RenderContext(cameraPos, rootLook, rootUp, rootViewMatrix); - renderingPortalView = true; - RenderSystem.backupProjectionMatrix(); - try { - logRenderCollection(minecraft, cameraPos, portals); + List portals = collectVisiblePortals(minecraft, cameraPos); + if (portals.isEmpty()) { + return; + } + if (portalViewShader == null) { + if (!loggedMissingPortalViewShader) { + loggedMissingPortalViewShader = true; + Antarchy.LOGGER.error("Portal gun destination view shader is unavailable; skipping destination render"); + } + minecraft.getMainRenderTarget().bindWrite(true); for (PortalGunPortalEntity portal : portals) { - PortalGunPortalEntity linkedPortal = portal.getLinkedPortal(); - if (linkedPortal == null || !linkedPortal.isAlive()) { - logRenderSkip(minecraft, portal); + drawPortalOverlay(minecraft, cameraPos, rootViewMatrix, portal); + } + return; + } + Vec3 effectiveCameraPos = cameraPos; + Matrix4f rootProjectionMatrix = new Matrix4f(projectionMatrix); + float partialTick = tickCounter.getGameTimeDeltaPartialTick(true); + int targetWidth = getPortalTargetWidth(minecraft); + int targetHeight = getPortalTargetHeight(minecraft); + IdentityHashMap rootScreenClips = new IdentityHashMap<>(); + for (PortalGunPortalEntity portal : portals) { + if (!portal.shouldRenderFront(effectiveCameraPos)) { + continue; + } + ScreenClip clip = computeScreenClip(cameraPos, rootViewMatrix, rootProjectionMatrix, portal, partialTick, targetWidth, targetHeight); + if (clip != null) { + rootScreenClips.put(portal, clip); + } + } + List renderablePortals = new ArrayList<>(); + for (PortalGunPortalEntity portal : portals) { + if (rootScreenClips.containsKey(portal)) { + renderablePortals.add(portal); + } + } + List selectedPortals = renderablePortals.stream() + .filter(portal -> { + PortalGunPortalEntity linked = portal.getLinkedPortal(); + return linked != null && linked.isAlive(); + }) + .sorted((left, right) -> Integer.compare(rootScreenClips.get(right).area(), rootScreenClips.get(left).area())) + .toList(); + Set selectedPortalSet = Collections.newSetFromMap(new IdentityHashMap<>()); + selectedPortalSet.addAll(selectedPortals); + List renderOrder = new ArrayList<>(portals.size()); + renderOrder.addAll(selectedPortals); + for (PortalGunPortalEntity portal : portals) { + if (!selectedPortalSet.contains(portal)) { + renderOrder.add(portal); + } + } + long linkedPortalCount = renderablePortals.stream() + .filter(portal -> portal.getLinkedPortal() != null && portal.getLinkedPortal().isAlive()) + .count(); + String candidateState = portals.stream() + .map(portal -> portal.getUUID() + ":" + (portal.getLinkedPortal() != null && portal.getLinkedPortal().isAlive() ? portal.getLinkedPortal().getUUID() : "unlinked") + ":" + rootScreenClips.containsKey(portal)) + .sorted() + .toList() + + "|selected=" + selectedPortals.stream().map(portal -> portal.getUUID().toString()).sorted().toList() + + "|shader=" + (portalViewShader != null) + + "|target=" + targetWidth + "x" + targetHeight; + if (!candidateState.equals(lastPortalCandidateState)) { + lastPortalCandidateState = candidateState; + if (!selectedPortals.isEmpty()) { + String selectedPairKey = selectedPortals.stream() + .map(portal -> portal.getUUID() + "->" + portal.getLinkedPortal().getUUID()) + .sorted() + .toList() + .toString(); + PortalSceneRenderTrace.arm(selectedPairKey); + } + Antarchy.LOGGER.info( + "Portal gun view candidate state changed portals={} frontFacing={} linked={} selected={} shader={} target={}x{}", + portals.size(), + renderablePortals.size(), + linkedPortalCount, + selectedPortals.size(), + portalViewShader != null, + targetWidth, + targetHeight + ); + } + if (selectedPortals.isEmpty()) { + minecraft.getMainRenderTarget().bindWrite(true); + for (PortalGunPortalEntity portal : portals) { + drawPortalOverlay(minecraft, cameraPos, rootViewMatrix, portal); + } + return; + } + RenderContext rootContext = new RenderContext(effectiveCameraPos, rootLook, rootUp, rootViewMatrix); + PortalViewBudget viewBudget = new PortalViewBudget(MAX_PORTAL_VIEWS_PER_FRAME); + renderingPortalView = true; + boolean projectionBackedUp = false; + try { + RenderSystem.backupProjectionMatrix(); + projectionBackedUp = true; + for (PortalGunPortalEntity portal : renderOrder) { + if (!selectedPortalSet.contains(portal)) { minecraft.getMainRenderTarget().bindWrite(true); - drawPortalOverlay(minecraft, cameraPos, poseMatrix, portal); + drawPortalOverlay(minecraft, cameraPos, rootViewMatrix, portal); + continue; + } + PortalGunPortalEntity linkedPortal = portal.getLinkedPortal(); + if (linkedPortal == null || !linkedPortal.isAlive()) { + minecraft.getMainRenderTarget().bindWrite(true); + drawPortalOverlay(minecraft, cameraPos, rootViewMatrix, portal); + continue; + } + boolean rendered; + try { + rendered = renderPortalSurfaceDirect(minecraft, cameraEntity, tickCounter, rootContext, rootProjectionMatrix, portal, linkedPortal, rootScreenClips.get(portal)); + } catch (RuntimeException exception) { + logRenderFailure(minecraft, exception); + RenderSystem.restoreProjectionMatrix(); + projectionBackedUp = false; + RenderSystem.backupProjectionMatrix(); + projectionBackedUp = true; + minecraft.getMainRenderTarget().bindWrite(true); + drawPortalOverlay(minecraft, cameraPos, rootViewMatrix, portal); + continue; + } + if (!rendered) { + minecraft.getMainRenderTarget().bindWrite(true); + drawPortalOverlay(minecraft, cameraPos, rootViewMatrix, portal); continue; } - int textureId = renderPortalSurface(minecraft, cameraEntity, tickCounter, rootContext, portal, linkedPortal, 0); RenderSystem.restoreProjectionMatrix(); + projectionBackedUp = false; RenderSystem.backupProjectionMatrix(); + projectionBackedUp = true; minecraft.getMainRenderTarget().bindWrite(true); - drawPortalQuad(cameraPos, poseMatrix, portal, textureId); - minecraft.getEntityRenderDispatcher().prepare(minecraft.level, camera, cameraEntity); - drawPortalOverlay(minecraft, cameraPos, poseMatrix, portal); + ScreenClip clip = rootScreenClips.get(portal); + String portalPairKey = portal.getUUID() + "->" + linkedPortal.getUUID(); + boolean composited; + try (PortalSceneRenderTrace.Phase ignored = PortalSceneRenderTrace.begin(portalPairKey, "direct-framebuffer", "target=" + targetWidth + "x" + targetHeight)) { + composited = true; + drawAnimatedPortalOverlay(minecraft, cameraPos, rootViewMatrix, portal); + } + if (composited && loggedPortalViewComposites.add(portalPairKey)) { + Antarchy.LOGGER.info("Portal gun destination view rendered directly source={} destination={} target={}x{}", portal.getId(), linkedPortal.getId(), targetWidth, targetHeight); + } } } catch (RuntimeException exception) { - Antarchy.LOGGER.error("Portal gun portal view render failed", exception); + logRenderFailure(minecraft, exception); } finally { - RenderSystem.restoreProjectionMatrix(); - renderingPortalView = false; - } - } - - private static void logRenderUnavailable(Minecraft minecraft, Camera camera, Matrix4f poseMatrix, DeltaTracker tickCounter) { - if (minecraft.level == null) { - return; - } - long gameTime = minecraft.level.getGameTime(); - if (lastHookLogTick != Long.MIN_VALUE && gameTime - lastHookLogTick < 100L) { - return; - } - lastHookLogTick = gameTime; - Antarchy.LOGGER.info("Portal gun render unavailable level={} gameRenderer={} camera={} pose={} tickCounter={}", minecraft.level != null, minecraft.gameRenderer != null, camera != null, poseMatrix != null, tickCounter != null); - } - - private static void logRenderHook(Minecraft minecraft, Camera camera) { - if (minecraft.level == null) { - return; - } - long gameTime = minecraft.level.getGameTime(); - if (lastHookLogTick != Long.MIN_VALUE && gameTime - lastHookLogTick < 100L) { - return; - } - lastHookLogTick = gameTime; - int portalCount = 0; - for (Entity entity : minecraft.level.entitiesForRendering()) { - if (entity instanceof PortalGunPortalEntity portal && portal.isAlive()) { - portalCount++; + try { + try { + restorePortalDrawState(); + } catch (RuntimeException exception) { + logRenderFailure(minecraft, exception); + } + try { + minecraft.getMainRenderTarget().bindWrite(true); + } catch (RuntimeException exception) { + logRenderFailure(minecraft, exception); + } + try { + minecraft.getEntityRenderDispatcher().prepare(minecraft.level, camera, minecraft.crosshairPickEntity); + } catch (RuntimeException exception) { + logRenderFailure(minecraft, exception); + } + try { + minecraft.levelRenderer.prepareCullFrustum(cameraPos, rootViewMatrix, rootProjectionMatrix); + } catch (RuntimeException exception) { + logRenderFailure(minecraft, exception); + } + if (projectionBackedUp) { + try { + RenderSystem.restoreProjectionMatrix(); + } catch (RuntimeException exception) { + logRenderFailure(minecraft, exception); + } + } + } finally { + renderingPortalView = false; } } - Antarchy.LOGGER.info("Portal gun render hook tick stage camera={} portals={}", camera.getPosition(), portalCount); - } - - private static List collectVisiblePortals(Minecraft minecraft, Camera camera) { - return collectVisiblePortals(minecraft, camera.getPosition()); } private static List collectVisiblePortals(Minecraft minecraft, Vec3 cameraPos) { - List portals = new ArrayList<>(); - for (Entity entity : minecraft.level.entitiesForRendering()) { - if (!(entity instanceof PortalGunPortalEntity portal) || !portal.isAlive()) { - continue; - } - if (portal.distanceToSqr(cameraPos) > 16384.0D) { - continue; - } - portals.add(portal); - } + AABB searchBounds = new AABB(cameraPos, cameraPos).inflate(128.0D); + List portals = minecraft.level.getEntitiesOfClass( + PortalGunPortalEntity.class, + searchBounds, + Entity::isAlive + ); + portals.removeIf(portal -> portal.distanceToSqr(cameraPos) > 16384.0D); portals.sort((left, right) -> Double.compare(left.distanceToSqr(cameraPos), right.distanceToSqr(cameraPos))); return portals; } - private static void logRenderEmpty(Minecraft minecraft, Vec3 cameraPos) { - if (minecraft.level == null) { - return; - } - long gameTime = minecraft.level.getGameTime(); - if (lastRenderLogTick != Long.MIN_VALUE && gameTime - lastRenderLogTick < 100L) { - return; - } - lastRenderLogTick = gameTime; - int portalCount = 0; - int linkedCount = 0; - int frontCount = 0; - for (Entity entity : minecraft.level.entitiesForRendering()) { - if (!(entity instanceof PortalGunPortalEntity portal) || !portal.isAlive()) { - continue; - } - portalCount++; - if (portal.getLinkedPortalId() != null) { - linkedCount++; - } - if (portal.shouldRenderFront(cameraPos)) { - frontCount++; - } - } - Antarchy.LOGGER.info("Portal gun render collected count=0 totalPortals={} linkedPortals={} frontPortals={} camera={}", portalCount, linkedCount, frontCount, cameraPos); - } - private static void logRenderCollection(Minecraft minecraft, Vec3 cameraPos, List portals) { - if (minecraft.level == null) { - return; - } - long gameTime = minecraft.level.getGameTime(); - if (lastRenderLogTick != Long.MIN_VALUE && gameTime - lastRenderLogTick < 100L) { - return; - } - lastRenderLogTick = gameTime; - Antarchy.LOGGER.info("Portal gun render collected count={} camera={}", portals.size(), cameraPos); - for (PortalGunPortalEntity portal : portals) { - PortalGunWorldPortalShape.PortalLocalCoords coords = portal.getWorldPortalShape().localCoords(cameraPos); - Antarchy.LOGGER.info("Portal gun render portal portal={} side={} linked={} center={} facing={} up={} cameraDepth={} cameraHorizontal={} cameraVertical={}", portal.getUUID(), portal.getPortalSide(), portal.getLinkedPortalId(), portal.position(), portal.getFacingDirection(), portal.getUpAxis(), coords.depth(), coords.horizontal(), coords.vertical()); - } - } - - private static void logRenderSkip(Minecraft minecraft, PortalGunPortalEntity portal) { - if (minecraft.level == null) { - return; - } - long gameTime = minecraft.level.getGameTime(); - if (lastRenderLogTick != Long.MIN_VALUE && gameTime - lastRenderLogTick < 20L) { - return; - } - lastRenderLogTick = gameTime; - Antarchy.LOGGER.info("Portal gun render skipped missing linked portal portal={} side={} linked={}", portal.getUUID(), portal.getPortalSide(), portal.getLinkedPortalId()); - } - - private static void ensureTargets(Minecraft minecraft) { - int width = minecraft.getWindow().getWidth(); - int height = minecraft.getWindow().getHeight(); + private static void ensureTarget(Minecraft minecraft, int depth) { + int width = getPortalTargetWidth(minecraft); + int height = getPortalTargetHeight(minecraft); if (width <= 0 || height <= 0) { return; } - for (int i = 0; i < PORTAL_TARGETS.length; i++) { - TextureTarget target = PORTAL_TARGETS[i]; - if (target == null) { - target = new TextureTarget(width, height, true, Minecraft.ON_OSX); - target.setClearColor(0.0F, 0.0F, 0.0F, 1.0F); - PORTAL_TARGETS[i] = target; - continue; + TextureTarget target = PORTAL_TARGETS[depth]; + if (target == null) { + target = new TextureTarget(width, height, true, Minecraft.ON_OSX); + target.setClearColor(0.0F, 0.0F, 0.0F, 1.0F); + PORTAL_TARGETS[depth] = target; + } else if (target.width != width || target.height != height) { + target.resize(width, height, Minecraft.ON_OSX); + } + target.setFilterMode(PORTAL_TARGET_FILTER); + } + + private static boolean ensureTargetSafely(Minecraft minecraft, int depth) { + int width = getPortalTargetWidth(minecraft); + int height = getPortalTargetHeight(minecraft); + TargetAllocationFailure failure = TARGET_ALLOCATION_FAILURES[depth]; + if (failure != null && failure.width() == width && failure.height() == height) { + return false; + } + try { + TARGET_ALLOCATION_FAILURES[depth] = null; + ensureTarget(minecraft, depth); + return PORTAL_TARGETS[depth] != null; + } catch (RuntimeException exception) { + TARGET_ALLOCATION_FAILURES[depth] = new TargetAllocationFailure(width, height); + Antarchy.LOGGER.error("Portal gun render target allocation failed at recursion depth {}", depth, exception); + return false; + } + } + + private static int getPortalTargetWidth(Minecraft minecraft) { + return Math.max(1, (int) Math.ceil(minecraft.getMainRenderTarget().width * RENDER_TARGET_SCALE)); + } + + private static int getPortalTargetHeight(Minecraft minecraft) { + return Math.max(1, (int) Math.ceil(minecraft.getMainRenderTarget().height * RENDER_TARGET_SCALE)); + } + + private static void logRenderFailure(Minecraft minecraft, RuntimeException exception) { + long gameTime = minecraft.level == null ? Long.MIN_VALUE : minecraft.level.getGameTime(); + if (lastRenderFailureTick == Long.MIN_VALUE || gameTime == Long.MIN_VALUE || gameTime - lastRenderFailureTick >= 100L) { + lastRenderFailureTick = gameTime; + Antarchy.LOGGER.error("Portal gun portal view render failed", exception); + } + } + + private static boolean renderPortalSurfaceDirect( + Minecraft minecraft, + Entity cameraEntity, + DeltaTracker tickCounter, + RenderContext currentContext, + Matrix4f baseProjectionMatrix, + PortalGunPortalEntity sourcePortal, + PortalGunPortalEntity destinationPortal, + ScreenClip clip + ) { + RenderTarget target = minecraft.getMainRenderTarget(); + if (clip == null || portalViewShader == null) { + return false; + } + int width = target.width; + int height = target.height; + if (depthBackup == null) { + depthBackup = new TextureTarget(width, height, true, Minecraft.ON_OSX); + } else if (depthBackup.width != width || depthBackup.height != height) { + depthBackup.resize(width, height, Minecraft.ON_OSX); + } + RenderContext transformedContext = transformContext(currentContext, sourcePortal, destinationPortal); + float partialTick = tickCounter.getGameTimeDeltaPartialTick(true); + Matrix4f clippedProjection = applyPortalClipPlane(baseProjectionMatrix, transformedContext, destinationPortal); + PortalCamera portalCamera = new PortalCamera(); + portalCamera.setup(minecraft.level, cameraEntity, partialTick, transformedContext.cameraPos(), transformedContext.look(), transformedContext.up()); + String portalPairKey = sourcePortal.getUUID() + "->" + destinationPortal.getUUID(); + LevelRenderer proxyRenderer = PortalGunPortalRendererPool.acquire(minecraft, destinationPortal.getUUID(), width, height); + LevelRendererPortalTargetsAccessor levelRendererTargets = (LevelRendererPortalTargetsAccessor) proxyRenderer; + PostChain entityEffect = levelRendererTargets.antarchy$getEntityEffect(); + PostChain transparencyChain = levelRendererTargets.antarchy$getTransparencyChain(); + PostChainTargetAccessor entityEffectTarget = entityEffect == null ? null : (PostChainTargetAccessor) entityEffect; + PostChainTargetAccessor transparencyTarget = transparencyChain == null ? null : (PostChainTargetAccessor) transparencyChain; + RenderTarget previousEntityEffectTarget = entityEffectTarget == null ? null : entityEffectTarget.antarchy$getScreenTarget(); + RenderTarget previousTransparencyTarget = transparencyTarget == null ? null : transparencyTarget.antarchy$getScreenTarget(); + PortalStencilTarget.Scope stencilScope = null; + boolean contextPushed = false; + try { + if (entityEffectTarget != null) { + entityEffectTarget.antarchy$setScreenTarget(target); } - if (target.width != width || target.height != height) { - target.resize(width, height, Minecraft.ON_OSX); + if (transparencyTarget != null) { + transparencyTarget.antarchy$setScreenTarget(target); + } + minecraft.gameRenderer.resetProjectionMatrix(clippedProjection); + int maskTexture = minecraft.getTextureManager().getTexture(BLUE_OVERLAY).getId(); + try (PortalSceneRenderTrace.Phase ignored = PortalSceneRenderTrace.begin(portalPairKey, "direct-stencil-mask", "clip=" + clip.width() + "x" + clip.height())) { + stencilScope = PortalStencilTarget.beginDirect(target, depthBackup, + () -> drawPortalQuad(currentContext.cameraPos(), currentContext.viewMatrix(), baseProjectionMatrix, width, height, sourcePortal, partialTick, maskTexture, clip)); + } + RenderSystem.enableScissor(clip.x(), clip.y(), clip.width(), clip.height()); + PortalGunPortalRenderState.pushPortalView(new PortalGunPortalRenderState.PortalRenderContext( + sourcePortal.getWorldPortalShape(), destinationPortal.getWorldPortalShape(), transformedContext.cameraPos(), + transformedContext.look(), transformedContext.up(), transformedContext.viewMatrix(), clippedProjection, + 0, true, destinationPortal.getUUID())); + contextPushed = true; + proxyRenderer.prepareCullFrustum(portalCamera.getPosition(), transformedContext.viewMatrix(), clippedProjection); + PortalGunPortalViewAreaManager.Scope areaScope = PortalGunPortalViewAreaManager.enter( + minecraft, proxyRenderer, destinationPortal.getUUID(), transformedContext.cameraPos(), portalCamera); + try { + try (PortalSceneRenderTrace.Phase ignored = PortalSceneRenderTrace.begin(portalPairKey, "direct-manual-scene", "target=" + width + "x" + height)) { + PortalGunProxySceneRenderer.render(minecraft, proxyRenderer, portalCamera, partialTick, + transformedContext.viewMatrix(), clippedProjection, portalPairKey); + } + } finally { + areaScope.close(); + } + PortalStencilTarget.activate(target); + RenderSystem.enableScissor(clip.x(), clip.y(), clip.width(), clip.height()); + minecraft.getEntityRenderDispatcher().prepare(minecraft.level, portalCamera, cameraEntity); + renderPortalTransitionEntities(minecraft, transformedContext.cameraPos(), partialTick, sourcePortal, destinationPortal); + return true; + } finally { + try { + if (stencilScope != null) { + stencilScope.close(); + } + if (contextPushed) { + PortalGunPortalRenderState.popPortalView(true); + } + } finally { + if (transparencyTarget != null) { + transparencyTarget.antarchy$setScreenTarget(previousTransparencyTarget); + } + if (entityEffectTarget != null) { + entityEffectTarget.antarchy$setScreenTarget(previousEntityEffectTarget); + } + restorePortalDrawState(); + target.bindWrite(true); + minecraft.gameRenderer.resetProjectionMatrix(baseProjectionMatrix); } } } @@ -237,81 +468,189 @@ public final class PortalGunPortalViewRenderer { Minecraft minecraft, Entity cameraEntity, DeltaTracker tickCounter, + PortalViewBudget viewBudget, RenderContext currentContext, + Matrix4f baseProjectionMatrix, PortalGunPortalEntity sourcePortal, PortalGunPortalEntity destinationPortal, - int depth + int depth, + ScreenClip precomputedClip ) { + String portalPairKey = sourcePortal.getUUID() + "->" + destinationPortal.getUUID(); + try (PortalSceneRenderTrace.Phase ignored = PortalSceneRenderTrace.begin(portalPairKey, "ensure-target", depth + ":" + getPortalTargetWidth(minecraft) + "x" + getPortalTargetHeight(minecraft))) { + if (!ensureTargetSafely(minecraft, depth)) { + return -1; + } + } TextureTarget target = PORTAL_TARGETS[depth]; RenderContext transformedContext = transformContext(currentContext, sourcePortal, destinationPortal); + float partialTick = tickCounter.getGameTimeDeltaPartialTick(true); + ScreenClip clip = precomputedClip == null + ? computeScreenClip(currentContext.cameraPos(), currentContext.viewMatrix(), baseProjectionMatrix, sourcePortal, partialTick, target.width, target.height) + : precomputedClip; + if (clip == null) { + return -1; + } + if (!viewBudget.tryReserve()) { + return -1; + } + Matrix4f clippedProjectionMatrix = applyPortalClipPlane(baseProjectionMatrix, transformedContext, destinationPortal); + boolean renderAll = true; PortalCamera portalCamera = new PortalCamera(); portalCamera.setup(minecraft.level, cameraEntity, tickCounter.getGameTimeDeltaPartialTick(true), transformedContext.cameraPos(), transformedContext.look(), transformedContext.up()); - target.bindWrite(true); - target.clear(Minecraft.ON_OSX); - double fov = minecraft.options.fov().get(); - Matrix4f baseProjectionMatrix = minecraft.gameRenderer.getProjectionMatrix(fov); - Matrix4f clippedProjectionMatrix = applyPortalClipPlane(baseProjectionMatrix, transformedContext, destinationPortal); - minecraft.gameRenderer.resetProjectionMatrix(clippedProjectionMatrix); - ScreenClip clip = computeScreenClip(minecraft, currentContext.cameraPos(), currentContext.viewMatrix(), baseProjectionMatrix, sourcePortal); - boolean clipEnabled = clip != null; - boolean renderAll = clip == null || clip.area() >= widthTimesHeight(minecraft); - if (clipEnabled) { - RenderSystem.enableScissor(clip.x(), clip.y(), clip.width(), clip.height()); + RenderTarget previousMainTarget = minecraft.getMainRenderTarget(); + MinecraftMainRenderTargetAccessor targetAccessor = (MinecraftMainRenderTargetAccessor) minecraft; + LevelRenderer proxyRenderer; + try (PortalSceneRenderTrace.Phase ignored = PortalSceneRenderTrace.begin(portalPairKey, "acquire-proxy", "target=" + target.width + "x" + target.height + " renderDistance=" + minecraft.options.getEffectiveRenderDistance())) { + proxyRenderer = PortalGunPortalRendererPool.acquire(minecraft, destinationPortal.getUUID(), target.width, target.height); } - PortalGunPortalRenderState.PortalRenderContext portalRenderContext = new PortalGunPortalRenderState.PortalRenderContext( - sourcePortal.getWorldPortalShape(), - destinationPortal.getWorldPortalShape(), - transformedContext.cameraPos(), - transformedContext.look(), - transformedContext.up(), - transformedContext.viewMatrix(), - clippedProjectionMatrix, - depth, - renderAll - ); - PortalGunPortalRenderState.pushPortalView(portalRenderContext); + LevelRendererPortalTargetsAccessor levelRendererTargets = (LevelRendererPortalTargetsAccessor) proxyRenderer; + PostChain entityEffect = levelRendererTargets.antarchy$getEntityEffect(); + PostChain transparencyChain = levelRendererTargets.antarchy$getTransparencyChain(); + PostChainTargetAccessor entityEffectTarget = entityEffect == null ? null : (PostChainTargetAccessor) entityEffect; + PostChainTargetAccessor transparencyTarget = transparencyChain == null ? null : (PostChainTargetAccessor) transparencyChain; + RenderTarget previousEntityEffectTarget = entityEffectTarget == null ? null : entityEffectTarget.antarchy$getScreenTarget(); + RenderTarget previousTransparencyTarget = transparencyTarget == null ? null : transparencyTarget.antarchy$getScreenTarget(); + targetAccessor.antarchy$setMainRenderTarget(target); + boolean portalRenderStatePushed = false; + PortalStencilTarget.Scope stencilScope = null; try { - minecraft.levelRenderer.prepareCullFrustum(portalCamera.getPosition(), transformedContext.viewMatrix(), clippedProjectionMatrix); - minecraft.levelRenderer.renderLevel(tickCounter, false, portalCamera, minecraft.gameRenderer, minecraft.gameRenderer.lightTexture(), transformedContext.viewMatrix(), clippedProjectionMatrix); - target.bindWrite(true); - minecraft.getEntityRenderDispatcher().prepare(minecraft.level, portalCamera, cameraEntity); - renderPortalTransitionEntities(minecraft, transformedContext.cameraPos(), tickCounter.getGameTimeDeltaPartialTick(true), sourcePortal, destinationPortal); + if (entityEffectTarget != null) { + entityEffectTarget.antarchy$setScreenTarget(target); + } + if (transparencyTarget != null) { + transparencyTarget.antarchy$setScreenTarget(target); + } + try (PortalSceneRenderTrace.Phase ignored = PortalSceneRenderTrace.begin(portalPairKey, "clear-and-stencil", "target=" + target.width + "x" + target.height + " clip=" + clip.width() + "x" + clip.height())) { + target.bindWrite(true); + RenderSystem.disableScissor(); + RenderSystem.depthMask(true); + RenderSystem.colorMask(true, true, true, true); + target.clear(Minecraft.ON_OSX); + minecraft.gameRenderer.resetProjectionMatrix(clippedProjectionMatrix); + int maskTextureId = minecraft.getTextureManager().getTexture(BLUE_OVERLAY).getId(); + stencilScope = PortalStencilTarget.begin( + target, + () -> drawPortalQuad(currentContext.cameraPos(), currentContext.viewMatrix(), baseProjectionMatrix, target.width, target.height, sourcePortal, partialTick, maskTextureId, clip) + ); + } + RenderSystem.enableScissor(clip.x(), clip.y(), clip.width(), clip.height()); + PortalGunPortalRenderState.PortalRenderContext portalRenderContext = new PortalGunPortalRenderState.PortalRenderContext( + sourcePortal.getWorldPortalShape(), + destinationPortal.getWorldPortalShape(), + transformedContext.cameraPos(), + transformedContext.look(), + transformedContext.up(), + transformedContext.viewMatrix(), + clippedProjectionMatrix, + depth, + renderAll, + destinationPortal.getUUID() + ); + PortalGunPortalRenderState.pushPortalView(portalRenderContext); + portalRenderStatePushed = true; + try (PortalSceneRenderTrace.Phase ignored = PortalSceneRenderTrace.begin(portalPairKey, "prepare-portal-view", "camera=" + transformedContext.cameraPos())) { + proxyRenderer.prepareCullFrustum(portalCamera.getPosition(), transformedContext.viewMatrix(), clippedProjectionMatrix); + } + try (PortalSceneRenderTrace.Phase ignored = PortalSceneRenderTrace.begin(portalPairKey, "activate-view-area", "destination=" + destinationPortal.getUUID())) { + PortalGunPortalViewAreaManager.Scope scope = PortalGunPortalViewAreaManager.enter(minecraft, proxyRenderer, destinationPortal.getUUID(), transformedContext.cameraPos(), portalCamera); + ignored.close(); + try { + try (PortalSceneRenderTrace.Phase sceneTrace = PortalSceneRenderTrace.begin(portalPairKey, "manual-scene-subpass", "target=" + target.width + "x" + target.height)) { + PortalGunProxySceneRenderer.render(minecraft, proxyRenderer, portalCamera, partialTick, transformedContext.viewMatrix(), clippedProjectionMatrix, portalPairKey); + } + } finally { + try (PortalSceneRenderTrace.Phase releaseTrace = PortalSceneRenderTrace.begin(portalPairKey, "release-view-area", "destination=" + destinationPortal.getUUID())) { + scope.close(); + } + } + } + PortalStencilTarget.activate(target); + RenderSystem.enableScissor(clip.x(), clip.y(), clip.width(), clip.height()); + try (PortalSceneRenderTrace.Phase ignored = PortalSceneRenderTrace.begin(portalPairKey, "transition-entities", "source=" + sourcePortal.getId())) { + target.bindWrite(true); + minecraft.getEntityRenderDispatcher().prepare(minecraft.level, portalCamera, cameraEntity); + renderPortalTransitionEntities(minecraft, transformedContext.cameraPos(), tickCounter.getGameTimeDeltaPartialTick(true), sourcePortal, destinationPortal); + } catch (RuntimeException exception) { + logRenderFailure(minecraft, exception); + } + if (loggedPortalSceneRenders.add(portalPairKey)) { + Antarchy.LOGGER.info("Portal gun destination scene render pass completed source={} destination={} depth={} target={}x{} texture={} scissor={}x{}+{},{} camera={}", sourcePortal.getId(), destinationPortal.getId(), depth, target.width, target.height, target.getColorTextureId(), clip.width(), clip.height(), clip.x(), clip.y(), transformedContext.cameraPos()); + } if (depth < MAX_RECURSION_DEPTH) { List nestedPortals = collectVisiblePortals(minecraft, transformedContext.cameraPos()); - Matrix4f nestedPose = new Matrix4f().identity(); for (PortalGunPortalEntity nestedPortal : nestedPortals) { + if (!viewBudget.hasRemaining()) { + break; + } PortalGunPortalEntity nestedLinked = nestedPortal.getLinkedPortal(); if (nestedLinked == null || !nestedLinked.isAlive() || nestedPortal == destinationPortal || nestedPortal == sourcePortal) { continue; } - if (clip != null && clip.area() < 6400) { + ScreenClip nestedClip = computeScreenClip(transformedContext.cameraPos(), transformedContext.viewMatrix(), baseProjectionMatrix, nestedPortal, partialTick, target.width, target.height); + if (nestedClip == null) { continue; } - int nestedTextureId = renderPortalSurface(minecraft, cameraEntity, tickCounter, transformedContext, nestedPortal, nestedLinked, depth + 1); - RenderSystem.restoreProjectionMatrix(); - RenderSystem.backupProjectionMatrix(); - target.bindWrite(true); - drawPortalQuad(transformedContext.cameraPos(), nestedPose, nestedPortal, nestedTextureId); - drawPortalOverlay(minecraft, transformedContext.cameraPos(), nestedPose, nestedPortal); + int nestedTextureId; + try { + nestedTextureId = renderPortalSurface(minecraft, cameraEntity, tickCounter, viewBudget, transformedContext, baseProjectionMatrix, nestedPortal, nestedLinked, depth + 1, nestedClip); + } catch (RuntimeException exception) { + logRenderFailure(minecraft, exception); + continue; + } + if (nestedTextureId < 0) { + continue; + } + try { + RenderSystem.restoreProjectionMatrix(); + RenderSystem.backupProjectionMatrix(); + target.bindWrite(true); + PortalStencilTarget.activate(target); + RenderSystem.enableScissor(clip.x(), clip.y(), clip.width(), clip.height()); + drawPortalQuad(transformedContext.cameraPos(), transformedContext.viewMatrix(), baseProjectionMatrix, target.width, target.height, nestedPortal, partialTick, nestedTextureId, nestedClip); + } catch (RuntimeException exception) { + logRenderFailure(minecraft, exception); + } } } } finally { - PortalGunPortalRenderState.popPortalView(renderAll); - } - if (clipEnabled) { - RenderSystem.disableScissor(); + try (PortalSceneRenderTrace.Phase ignored = PortalSceneRenderTrace.begin(portalPairKey, "restore-render-state", "depth=" + depth)) { + try { + if (stencilScope != null) { + stencilScope.close(); + } + if (portalRenderStatePushed) { + PortalGunPortalRenderState.popPortalView(renderAll); + } + } finally { + try { + restorePortalDrawState(); + } finally { + try { + if (transparencyTarget != null) { + transparencyTarget.antarchy$setScreenTarget(previousTransparencyTarget); + } + } finally { + try { + if (entityEffectTarget != null) { + entityEffectTarget.antarchy$setScreenTarget(previousEntityEffectTarget); + } + } finally { + targetAccessor.antarchy$setMainRenderTarget(previousMainTarget); + previousMainTarget.bindWrite(true); + } + } + } + } + } } return target.getColorTextureId(); } private static RenderContext transformContext(RenderContext currentContext, PortalGunPortalEntity sourcePortal, PortalGunPortalEntity destinationPortal) { - Vec3 destinationNormal = destinationPortal.getNormalVec().normalize(); Vec3 relativeEye = currentContext.cameraPos().subtract(sourcePortal.position()); - double depthOffset = Math.max(CAMERA_OFFSET, relativeEye.dot(sourcePortal.getNormalVec().normalize())); Vec3 transformedEyeOffset = PortalGunTransformUtil.transformRelativePosition(sourcePortal, destinationPortal, relativeEye); - Vec3 destinationEye = destinationPortal.position() - .add(transformedEyeOffset) - .add(destinationNormal.scale(depthOffset + CAMERA_OFFSET - transformedEyeOffset.dot(destinationNormal))); + Vec3 destinationEye = destinationPortal.position().add(transformedEyeOffset); Vec3 transformedLook = PortalGunTransformUtil.transformVector(sourcePortal, destinationPortal, currentContext.look()).normalize(); Vec3 transformedUp = PortalGunTransformUtil.transformVector(sourcePortal, destinationPortal, currentContext.up()).normalize(); Matrix4f viewMatrix = new Matrix4f().rotation(PortalGunTransformUtil.orientationQuaternion(transformedLook, transformedUp).conjugate(new Quaternionf())); @@ -326,13 +665,13 @@ public final class PortalGunPortalViewRenderer { return projectionMatrix; } Matrix4f clippedProjection = new Matrix4f(projectionMatrix); - Vector4f q = new Vector4f( - signNonZero(clipPlane.x()) / clippedProjection.m00(), - signNonZero(clipPlane.y()) / clippedProjection.m11(), - -1.0F, - (1.0F + clippedProjection.m22()) / clippedProjection.m32() - ); - float scale = 2.0F / clipPlane.dot(q); + Vector4f q = new Vector4f(signNonZero(clipPlane.x()), signNonZero(clipPlane.y()), 1.0F, 1.0F) + .mul(new Matrix4f(projectionMatrix).invert()); + float planeCornerDot = clipPlane.dot(q); + if (!Float.isFinite(planeCornerDot) || Math.abs(planeCornerDot) <= 1.0E-6F) { + return projectionMatrix; + } + float scale = 2.0F / planeCornerDot; Vector4f c = clipPlane.mul(scale, new Vector4f()); clippedProjection.m02(c.x()); clippedProjection.m12(c.y()); @@ -344,7 +683,7 @@ public final class PortalGunPortalViewRenderer { private static Vector4f portalPlaneToCameraSpace(RenderContext renderContext, Vec3 planePointWorld, Vec3 planeNormalWorld) { Vec3 look = renderContext.look().normalize(); Vec3 up = renderContext.up().normalize(); - Vec3 right = up.cross(look).normalize(); + Vec3 right = look.cross(up).normalize(); Vec3 relativePoint = planePointWorld.subtract(renderContext.cameraPos()); Vector4f point = new Vector4f( (float) relativePoint.dot(right), @@ -368,29 +707,28 @@ public final class PortalGunPortalViewRenderer { return value >= 0.0F ? 1.0F : -1.0F; } - private static ScreenClip computeScreenClip(Minecraft minecraft, Vec3 cameraPos, Matrix4f viewMatrix, Matrix4f projectionMatrix, PortalGunPortalEntity portal) { - Vec3[] worldCorners = portal.getWorldPortalShape().getCorners(SURFACE_OFFSET); - int width = minecraft.getWindow().getWidth(); - int height = minecraft.getWindow().getHeight(); + private static ScreenClip computeScreenClip(Vec3 cameraPos, Matrix4f viewMatrix, Matrix4f projectionMatrix, PortalGunPortalEntity portal, float partialTick, int width, int height) { + PortalGunWorldPortalShape renderShape = portal.getWorldPortalShape().scaleAperture(portal.getPortalVisualScale(partialTick)); + Vec3[] worldCorners = renderShape.getCorners(SURFACE_OFFSET); + List clipVertices = new java.util.ArrayList<>(worldCorners.length); + Matrix4f combined = new Matrix4f(projectionMatrix).mul(viewMatrix); + for (Vec3 worldCorner : worldCorners) { + Vec3 corner = worldCorner.subtract(cameraPos); + clipVertices.add(new Vector4f((float) corner.x, (float) corner.y, (float) corner.z, 1.0F).mul(combined)); + } + clipVertices = clipPolygon(clipVertices, false); + clipVertices = clipPolygon(clipVertices, true); + if (clipVertices.size() < 3) { + return null; + } float minX = Float.POSITIVE_INFINITY; float minY = Float.POSITIVE_INFINITY; float maxX = Float.NEGATIVE_INFINITY; float maxY = Float.NEGATIVE_INFINITY; - boolean visible = false; - Matrix4f combined = new Matrix4f(projectionMatrix).mul(viewMatrix); - for (Vec3 worldCorner : worldCorners) { - Vec3 corner = worldCorner.subtract(cameraPos); - Vector4f projected = new Vector4f((float) corner.x, (float) corner.y, (float) corner.z, 1.0F).mul(combined); - if (projected.w <= 0.001F) { - continue; - } + for (Vector4f projected : clipVertices) { float invW = 1.0F / projected.w; float ndcX = projected.x * invW; float ndcY = projected.y * invW; - if (ndcX < -1.25F || ndcX > 1.25F || ndcY < -1.25F || ndcY > 1.25F) { - continue; - } - visible = true; float screenX = (ndcX * 0.5F + 0.5F) * width; float screenY = (ndcY * 0.5F + 0.5F) * height; minX = Math.min(minX, screenX); @@ -398,21 +736,119 @@ public final class PortalGunPortalViewRenderer { maxX = Math.max(maxX, screenX); maxY = Math.max(maxY, screenY); } - if (!visible) { + if (maxX <= 0.0F || minX >= width || maxY <= 0.0F || minY >= height) { return null; } int scissorX = Mth.clamp((int) Math.floor(minX), 0, width - 1); int scissorMaxX = Mth.clamp((int) Math.ceil(maxX), scissorX + 1, width); int scissorYTop = Mth.clamp((int) Math.floor(minY), 0, height - 1); int scissorYBottom = Mth.clamp((int) Math.ceil(maxY), scissorYTop + 1, height); - int scissorY = height - scissorYBottom; + int scissorY = scissorYTop; int scissorWidth = Math.max(1, scissorMaxX - scissorX); int scissorHeight = Math.max(1, scissorYBottom - scissorYTop); return new ScreenClip(scissorX, scissorY, scissorWidth, scissorHeight); } - private static void drawPortalQuad(Vec3 cameraPos, Matrix4f poseMatrix, PortalGunPortalEntity portal, int textureId) { - drawTexturedPortal(cameraPos, poseMatrix, portal, textureId, SURFACE_OFFSET, 1.0F, 1.0F, 1.0F, 1.0F, false); + private static List clipPolygon(List vertices, boolean nearPlane) { + if (vertices.isEmpty()) { + return vertices; + } + List clipped = new java.util.ArrayList<>(vertices.size() + 2); + Vector4f previous = vertices.get(vertices.size() - 1); + float previousDistance = clipPlaneDistance(previous, nearPlane); + for (Vector4f current : vertices) { + float currentDistance = clipPlaneDistance(current, nearPlane); + boolean previousInside = previousDistance >= 0.0F; + boolean currentInside = currentDistance >= 0.0F; + if (previousInside != currentInside) { + float amount = previousDistance / (previousDistance - currentDistance); + clipped.add(new Vector4f( + previous.x + (current.x - previous.x) * amount, + previous.y + (current.y - previous.y) * amount, + previous.z + (current.z - previous.z) * amount, + previous.w + (current.w - previous.w) * amount + )); + } + if (currentInside) { + clipped.add(new Vector4f(current)); + } + previous = current; + previousDistance = currentDistance; + } + return clipped; + } + + private static float clipPlaneDistance(Vector4f vertex, boolean nearPlane) { + return nearPlane ? vertex.z + vertex.w : vertex.w - 0.001F; + } + + private static boolean drawPortalQuad(Vec3 cameraPos, Matrix4f viewMatrix, Matrix4f projectionMatrix, int width, int height, PortalGunPortalEntity portal, float partialTick, int textureId, ScreenClip clip) { + if (portalViewShader == null) { + if (!loggedMissingPortalViewShader) { + loggedMissingPortalViewShader = true; + Antarchy.LOGGER.error("Portal gun destination view shader became unavailable; skipping destination composite"); + } + return false; + } + if (clip == null || width <= 0 || height <= 0) { + return false; + } + PortalGunWorldPortalShape shape = portal.getWorldPortalShape().scaleAperture(portal.getPortalVisualScale(partialTick)); + Matrix4f combined = new Matrix4f(projectionMatrix).mul(viewMatrix); + double apertureInset = (1.0D - PORTAL_APERTURE_SCALE) * 0.5D; + double apertureStart = apertureInset; + double apertureEnd = 1.0D - apertureInset; + List polygon = new ArrayList<>(4); + polygon.add(projectPortalVertex(combined, cameraPos, shape, apertureStart, apertureStart)); + polygon.add(projectPortalVertex(combined, cameraPos, shape, apertureEnd, apertureStart)); + polygon.add(projectPortalVertex(combined, cameraPos, shape, apertureEnd, apertureEnd)); + polygon.add(projectPortalVertex(combined, cameraPos, shape, apertureStart, apertureEnd)); + polygon = clipPolygon(polygon, false); + polygon = clipPolygon(polygon, true); + if (polygon.size() < 3) { + return false; + } + BufferBuilder buffer = Tesselator.getInstance().begin(VertexFormat.Mode.TRIANGLES, DefaultVertexFormat.POSITION_TEX); + for (int index = 1; index + 1 < polygon.size(); index++) { + addPortalViewVertex(buffer, polygon.get(0)); + addPortalViewVertex(buffer, polygon.get(index)); + addPortalViewVertex(buffer, polygon.get(index + 1)); + } + MeshData mesh = buffer.build(); + if (mesh == null) { + return false; + } + try { + RenderSystem.disableBlend(); + RenderSystem.depthMask(false); + RenderSystem.enableDepthTest(); + RenderSystem.disableCull(); + portalViewShader.setSampler("Sampler0", textureId); + RenderSystem.setShader(() -> portalViewShader); + RenderSystem.setShaderTexture(0, textureId); + RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F); + BufferUploader.drawWithShader(mesh); + } finally { + restorePortalDrawState(); + } + return true; + } + + private static Vector4f projectPortalVertex(Matrix4f combined, Vec3 cameraPos, PortalGunWorldPortalShape shape, double u, double v) { + Vec3 worldPosition = shape.center() + .add(shape.right().scale((u * 2.0D - 1.0D) * shape.halfWidth())) + .add(shape.up().scale((v * 2.0D - 1.0D) * shape.halfHeight())) + .add(shape.normal().scale(SURFACE_OFFSET)); + Vec3 relative = worldPosition.subtract(cameraPos); + return new Vector4f((float) relative.x, (float) relative.y, (float) relative.z, 1.0F).mul(combined); + } + + private static void addPortalViewVertex(BufferBuilder buffer, Vector4f clipPosition) { + float inverseW = 1.0F / clipPosition.w(); + float ndcX = clipPosition.x() * inverseW; + float ndcY = clipPosition.y() * inverseW; + float ndcZ = clipPosition.z() * inverseW; + buffer.addVertex(ndcX, ndcY, ndcZ).setUv(ndcX * 0.5F + 0.5F, ndcY * 0.5F + 0.5F); } private static void renderPortalTransitionEntities(Minecraft minecraft, Vec3 cameraPos, float partialTick, PortalGunPortalEntity sourcePortal, PortalGunPortalEntity destinationPortal) { @@ -421,18 +857,30 @@ public final class PortalGunPortalViewRenderer { } MultiBufferSource.BufferSource bufferSource = minecraft.renderBuffers().bufferSource(); PoseStack poseStack = new PoseStack(); - for (Entity entity : minecraft.level.entitiesForRendering()) { - if (!shouldRenderTransitionEntity(entity, sourcePortal, destinationPortal)) { - continue; - } - renderTransitionEntity(minecraft, cameraPos, partialTick, sourcePortal, destinationPortal, entity, poseStack, bufferSource); - for (Entity passenger : entity.getPassengers()) { - if (shouldRenderTransitionEntity(passenger, sourcePortal, destinationPortal)) { - renderTransitionEntity(minecraft, cameraPos, partialTick, sourcePortal, destinationPortal, passenger, poseStack, bufferSource); - } + AABB transitionBounds = sourcePortal.getWorldPortalShape().getBoundsForCulling(); + List renderEntities = minecraft.level.getEntities( + (Entity) null, + transitionBounds, + entity -> shouldRenderTransitionEntity(entity, sourcePortal, destinationPortal) + ); + Set renderedEntities = Collections.newSetFromMap(new IdentityHashMap<>()); + try { + for (Entity entity : renderEntities) { + renderTransitionEntityTree(minecraft, cameraPos, partialTick, sourcePortal, destinationPortal, entity, poseStack, bufferSource, renderedEntities); } + } finally { + bufferSource.endBatch(); + } + } + + private static void renderTransitionEntityTree(Minecraft minecraft, Vec3 cameraPos, float partialTick, PortalGunPortalEntity sourcePortal, PortalGunPortalEntity destinationPortal, Entity entity, PoseStack poseStack, MultiBufferSource.BufferSource bufferSource, Set renderedEntities) { + if (!shouldRenderTransitionEntity(entity, sourcePortal, destinationPortal) || !renderedEntities.add(entity)) { + return; + } + renderTransitionEntity(minecraft, cameraPos, partialTick, sourcePortal, destinationPortal, entity, poseStack, bufferSource); + for (Entity passenger : entity.getPassengers()) { + renderTransitionEntityTree(minecraft, cameraPos, partialTick, sourcePortal, destinationPortal, passenger, poseStack, bufferSource, renderedEntities); } - bufferSource.endBatch(); } private static boolean shouldRenderTransitionEntity(Entity entity, PortalGunPortalEntity sourcePortal, PortalGunPortalEntity destinationPortal) { @@ -449,8 +897,7 @@ public final class PortalGunPortalViewRenderer { transformationStack.push(); try { Vec3 transformedPosition = transformationStack.moveEntity(sourcePortal, destinationPortal, partialTick); - Vec3 transformedLook = PortalGunTransformUtil.transformVector(sourcePortal, destinationPortal, entity.getLookAngle()).normalize(); - float yaw = PortalGunTransformUtil.yawFromLook(transformedLook); + float yaw = Mth.rotLerp(partialTick, entity.yRotO, entity.getYRot()); int light = minecraft.getEntityRenderDispatcher().getPackedLightCoords(entity, partialTick); RenderSystem.enableDepthTest(); RenderSystem.depthMask(true); @@ -475,11 +922,11 @@ public final class PortalGunPortalViewRenderer { } } - private static int widthTimesHeight(Minecraft minecraft) { - return minecraft.getWindow().getWidth() * minecraft.getWindow().getHeight(); - } - private static void drawPortalOverlay(Minecraft minecraft, Vec3 cameraPos, Matrix4f poseMatrix, PortalGunPortalEntity portal) { + if (!portal.shouldRenderFront(cameraPos)) { + return; + } + RenderSystem.disableScissor(); float time = (minecraft.level.getGameTime() + minecraft.getTimer().getGameTimeDeltaPartialTick(false)) * 0.075F; float pulse = 0.72F + 0.18F * (float) Math.sin(time); float edge = 0.38F + 0.08F * (float) Math.cos(time * 1.7F); @@ -489,15 +936,65 @@ public final class PortalGunPortalViewRenderer { if (portal.getPortalSide() == PortalGunPortalEntity.PortalSide.BLUE) { drawTexturedPortal(cameraPos, poseMatrix, portal, BLUE_OVERLAY, OVERLAY_OFFSET, 0.78F, 0.94F, 1.0F, pulse, false); drawTexturedPortal(cameraPos, poseMatrix, portal, BLUE_OVERLAY, OVERLAY_OFFSET + 0.0008D, channelRed, channelGreen, channelBlue, edge, true); + drawAnimatedPortalOverlay(minecraft, cameraPos, poseMatrix, portal); return; } drawTexturedPortal(cameraPos, poseMatrix, portal, ORANGE_OVERLAY, OVERLAY_OFFSET, 1.0F, 0.84F, 0.46F, pulse, false); drawTexturedPortal(cameraPos, poseMatrix, portal, ORANGE_OVERLAY, OVERLAY_OFFSET + 0.0008D, channelRed, channelGreen, channelBlue, edge, true); + drawAnimatedPortalOverlay(minecraft, cameraPos, poseMatrix, portal); + } + + private static void drawAnimatedPortalOverlay(Minecraft minecraft, Vec3 cameraPos, Matrix4f poseMatrix, PortalGunPortalEntity portal) { + if (!AntarchySettings.portalGunFancyPortals() + || minecraft.options.graphicsMode().get() != GraphicsStatus.FANCY) { + return; + } + ResourceLocation texture = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, + "textures/entity/portal_gun/" + (portal.getPortalSide() == PortalGunPortalEntity.PortalSide.BLUE + ? "portal_blue_vortex.png" + : "portal_orange_vortex.png")); + float partialTick = minecraft.getTimer().getGameTimeDeltaPartialTick(true); + float portalScale = portal.getPortalVisualScale(partialTick); + Vec3 widthVec = portal.getWidthVec().normalize().scale(portal.getPortalWidth() / 2.0D * portalScale); + Vec3 upVec = portal.getUpVec().normalize().scale(portal.getPortalHeight() / 2.0D * portalScale); + Vec3 normalVec = portal.getNormalVec().normalize().scale(OVERLAY_OFFSET + 0.0016D); + Vec3 center = portal.position().add(normalVec); + Vec3 bottomLeft = center.subtract(widthVec).subtract(upVec).subtract(cameraPos); + Vec3 topLeft = center.subtract(widthVec).add(upVec).subtract(cameraPos); + Vec3 topRight = center.add(widthVec).add(upVec).subtract(cameraPos); + Vec3 bottomRight = center.add(widthVec).subtract(upVec).subtract(cameraPos); + float red = portal.getPortalSide() == PortalGunPortalEntity.PortalSide.BLUE ? 0.78F : 1.0F; + float green = portal.getPortalSide() == PortalGunPortalEntity.PortalSide.BLUE ? 0.94F : 0.84F; + float blue = portal.getPortalSide() == PortalGunPortalEntity.PortalSide.BLUE ? 1.0F : 0.46F; + float frameV = Math.floorMod((int) ((minecraft.level.getGameTime() + minecraft.getTimer().getGameTimeDeltaPartialTick(false)) / 5.0F), 4) * 0.25F; + float frameBottomV = frameV + 0.25F; + BufferBuilder buffer = Tesselator.getInstance().begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_TEX); + buffer.addVertex(poseMatrix, (float) bottomLeft.x, (float) bottomLeft.y, (float) bottomLeft.z).setUv(0.0F, frameBottomV); + buffer.addVertex(poseMatrix, (float) topLeft.x, (float) topLeft.y, (float) topLeft.z).setUv(0.0F, frameV); + buffer.addVertex(poseMatrix, (float) topRight.x, (float) topRight.y, (float) topRight.z).setUv(1.0F, frameV); + buffer.addVertex(poseMatrix, (float) bottomRight.x, (float) bottomRight.y, (float) bottomRight.z).setUv(1.0F, frameBottomV); + MeshData mesh = buffer.build(); + try { + RenderSystem.enableBlend(); + RenderSystem.defaultBlendFunc(); + RenderSystem.depthMask(false); + RenderSystem.enableDepthTest(); + RenderSystem.disableCull(); + RenderSystem.setShader(GameRenderer::getPositionTexShader); + RenderSystem.setShaderTexture(0, texture); + RenderSystem.setShaderColor(red, green, blue, 0.92F); + if (mesh != null) { + BufferUploader.drawWithShader(mesh); + } + } finally { + restorePortalDrawState(); + } } private static void drawTexturedPortal(Vec3 cameraPos, Matrix4f poseMatrix, PortalGunPortalEntity portal, ResourceLocation texture, double offset, float red, float green, float blue, float alpha, boolean additive) { - Vec3 widthVec = portal.getWidthVec().normalize().scale(0.5D); - Vec3 upVec = portal.getUpVec().normalize().scale(1.0D); + float portalScale = portal.getPortalVisualScale(Minecraft.getInstance().getTimer().getGameTimeDeltaPartialTick(true)); + Vec3 widthVec = portal.getWidthVec().normalize().scale(portal.getPortalWidth() / 2.0D * portalScale); + Vec3 upVec = portal.getUpVec().normalize().scale(portal.getPortalHeight() / 2.0D * portalScale); Vec3 normalVec = portal.getNormalVec().normalize().scale(offset); Vec3 center = portal.position().add(normalVec); Vec3 bottomLeft = center.subtract(widthVec).subtract(upVec).subtract(cameraPos); @@ -505,39 +1002,37 @@ public final class PortalGunPortalViewRenderer { Vec3 topRight = center.add(widthVec).add(upVec).subtract(cameraPos); Vec3 bottomRight = center.add(widthVec).subtract(upVec).subtract(cameraPos); - RenderSystem.enableBlend(); - if (additive) { - RenderSystem.blendFuncSeparate(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE, GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ZERO); - } else { - RenderSystem.defaultBlendFunc(); - } - RenderSystem.depthMask(false); - RenderSystem.disableDepthTest(); - RenderSystem.disableCull(); - RenderSystem.setShader(GameRenderer::getPositionTexShader); - RenderSystem.setShaderTexture(0, texture); - RenderSystem.setShaderColor(red, green, blue, alpha); - BufferBuilder buffer = Tesselator.getInstance().begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_TEX); buffer.addVertex(poseMatrix, (float) bottomLeft.x, (float) bottomLeft.y, (float) bottomLeft.z).setUv(0.0F, 1.0F); buffer.addVertex(poseMatrix, (float) topLeft.x, (float) topLeft.y, (float) topLeft.z).setUv(0.0F, 0.0F); buffer.addVertex(poseMatrix, (float) topRight.x, (float) topRight.y, (float) topRight.z).setUv(1.0F, 0.0F); buffer.addVertex(poseMatrix, (float) bottomRight.x, (float) bottomRight.y, (float) bottomRight.z).setUv(1.0F, 1.0F); MeshData mesh = buffer.build(); - if (mesh != null) { - BufferUploader.drawWithShader(mesh); + try { + RenderSystem.enableBlend(); + if (additive) { + RenderSystem.blendFuncSeparate(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE, GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ZERO); + } else { + RenderSystem.defaultBlendFunc(); + } + RenderSystem.depthMask(false); + RenderSystem.enableDepthTest(); + RenderSystem.disableCull(); + RenderSystem.setShader(GameRenderer::getPositionTexShader); + RenderSystem.setShaderTexture(0, texture); + RenderSystem.setShaderColor(red, green, blue, alpha); + if (mesh != null) { + BufferUploader.drawWithShader(mesh); + } + } finally { + restorePortalDrawState(); } - - RenderSystem.enableCull(); - RenderSystem.enableDepthTest(); - RenderSystem.depthMask(true); - RenderSystem.disableBlend(); - RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F); } - private static void drawTexturedPortal(Vec3 cameraPos, Matrix4f poseMatrix, PortalGunPortalEntity portal, int textureId, double offset, float red, float green, float blue, float alpha, boolean additive) { - Vec3 widthVec = portal.getWidthVec().normalize().scale(0.5D); - Vec3 upVec = portal.getUpVec().normalize().scale(1.0D); + private static void drawTexturedPortal(Vec3 cameraPos, Matrix4f poseMatrix, PortalGunPortalEntity portal, int textureId, double offset, float red, float green, float blue, float alpha, boolean additive, float bottomLeftU, float bottomLeftV, float topLeftU, float topLeftV, float topRightU, float topRightV, float bottomRightU, float bottomRightV) { + float portalScale = portal.getPortalVisualScale(Minecraft.getInstance().getTimer().getGameTimeDeltaPartialTick(true)); + Vec3 widthVec = portal.getWidthVec().normalize().scale(portal.getPortalWidth() / 2.0D * portalScale); + Vec3 upVec = portal.getUpVec().normalize().scale(portal.getPortalHeight() / 2.0D * portalScale); Vec3 normalVec = portal.getNormalVec().normalize().scale(offset); Vec3 center = portal.position().add(normalVec); Vec3 bottomLeft = center.subtract(widthVec).subtract(upVec).subtract(cameraPos); @@ -545,32 +1040,40 @@ public final class PortalGunPortalViewRenderer { Vec3 topRight = center.add(widthVec).add(upVec).subtract(cameraPos); Vec3 bottomRight = center.add(widthVec).subtract(upVec).subtract(cameraPos); - RenderSystem.enableBlend(); - if (additive) { - RenderSystem.blendFuncSeparate(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE, GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ZERO); - } else { - RenderSystem.defaultBlendFunc(); - } - RenderSystem.depthMask(false); - RenderSystem.disableDepthTest(); - RenderSystem.disableCull(); - RenderSystem.setShader(GameRenderer::getPositionTexShader); - RenderSystem.setShaderTexture(0, textureId); - RenderSystem.setShaderColor(red, green, blue, alpha); - BufferBuilder buffer = Tesselator.getInstance().begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_TEX); - buffer.addVertex(poseMatrix, (float) bottomLeft.x, (float) bottomLeft.y, (float) bottomLeft.z).setUv(0.0F, 1.0F); - buffer.addVertex(poseMatrix, (float) topLeft.x, (float) topLeft.y, (float) topLeft.z).setUv(0.0F, 0.0F); - buffer.addVertex(poseMatrix, (float) topRight.x, (float) topRight.y, (float) topRight.z).setUv(1.0F, 0.0F); - buffer.addVertex(poseMatrix, (float) bottomRight.x, (float) bottomRight.y, (float) bottomRight.z).setUv(1.0F, 1.0F); + buffer.addVertex(poseMatrix, (float) bottomLeft.x, (float) bottomLeft.y, (float) bottomLeft.z).setUv(bottomLeftU, bottomLeftV); + buffer.addVertex(poseMatrix, (float) topLeft.x, (float) topLeft.y, (float) topLeft.z).setUv(topLeftU, topLeftV); + buffer.addVertex(poseMatrix, (float) topRight.x, (float) topRight.y, (float) topRight.z).setUv(topRightU, topRightV); + buffer.addVertex(poseMatrix, (float) bottomRight.x, (float) bottomRight.y, (float) bottomRight.z).setUv(bottomRightU, bottomRightV); MeshData mesh = buffer.build(); - if (mesh != null) { - BufferUploader.drawWithShader(mesh); + try { + RenderSystem.enableBlend(); + if (additive) { + RenderSystem.blendFuncSeparate(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE, GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ZERO); + } else { + RenderSystem.defaultBlendFunc(); + } + RenderSystem.depthMask(false); + RenderSystem.enableDepthTest(); + RenderSystem.disableCull(); + RenderSystem.setShader(GameRenderer::getPositionTexShader); + RenderSystem.setShaderTexture(0, textureId); + RenderSystem.setShaderColor(red, green, blue, alpha); + if (mesh != null) { + BufferUploader.drawWithShader(mesh); + } + } finally { + restorePortalDrawState(); } + } + private static void restorePortalDrawState() { + RenderSystem.disableScissor(); RenderSystem.enableCull(); RenderSystem.enableDepthTest(); RenderSystem.depthMask(true); + RenderSystem.colorMask(true, true, true, true); + RenderSystem.defaultBlendFunc(); RenderSystem.disableBlend(); RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F); } @@ -584,19 +1087,44 @@ public final class PortalGunPortalViewRenderer { float pitch = PortalGunTransformUtil.pitchFromLook(normalizedLook); this.setPosition(position); this.setRotation(yaw, pitch); - Vec3 currentUp = new Vec3(this.getUpVector()).normalize(); - if (currentUp.dot(normalizedUp) < 0.0D) { - this.setRotation(yaw + 180.0F, -pitch); - } + Quaternionf orientation = PortalGunTransformUtil.orientationQuaternion(normalizedLook, normalizedUp); + this.rotation().set(orientation); + CameraBasisAccessor basis = (CameraBasisAccessor) (Object) this; + basis.antarchy$getForwards().set(0.0F, 0.0F, -1.0F).rotate(orientation); + basis.antarchy$getUp().set(0.0F, 1.0F, 0.0F).rotate(orientation); + basis.antarchy$getLeft().set(-1.0F, 0.0F, 0.0F).rotate(orientation); } } private record RenderContext(Vec3 cameraPos, Vec3 look, Vec3 up, Matrix4f viewMatrix) { } + private static final class PortalViewBudget { + private int remaining; + + private PortalViewBudget(int maximumViews) { + this.remaining = maximumViews; + } + + private boolean tryReserve() { + if (this.remaining <= 0) { + return false; + } + this.remaining--; + return true; + } + + private boolean hasRemaining() { + return this.remaining > 0; + } + } + private record ScreenClip(int x, int y, int width, int height) { private int area() { return this.width * this.height; } } + + private record TargetAllocationFailure(int width, int height) { + } } diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/PortalGunProxySceneRenderer.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/PortalGunProxySceneRenderer.java new file mode 100644 index 00000000..71a18308 --- /dev/null +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/PortalGunProxySceneRenderer.java @@ -0,0 +1,193 @@ +package com.craisinlord.antarchy.content.client.renderer; + +import com.craisinlord.antarchy.mixins.client.LevelRendererPortalSceneInvoker; +import com.craisinlord.antarchy.mixins.client.LevelRendererPortalViewAreaAccessor; +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.platform.Lighting; +import com.mojang.blaze3d.vertex.PoseStack; +import net.minecraft.client.Camera; +import net.minecraft.client.CloudStatus; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.FogRenderer; +import net.minecraft.client.renderer.LevelRenderer; +import net.minecraft.client.renderer.chunk.SectionRenderDispatcher; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.client.particle.ParticleRenderType; +import net.minecraft.client.renderer.culling.Frustum; +import net.minecraft.core.BlockPos; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.phys.Vec3; +import net.minecraft.world.phys.AABB; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import org.joml.Matrix4f; +import org.joml.Matrix4fStack; + +public final class PortalGunProxySceneRenderer { + private PortalGunProxySceneRenderer() { + } + + public static void render(Minecraft minecraft, LevelRenderer renderer, Camera camera, float partialTick, Matrix4f modelViewMatrix, Matrix4f projectionMatrix, String tracePair) { + Vec3 cameraPosition = camera.getPosition(); + double cameraX = cameraPosition.x; + double cameraY = cameraPosition.y; + double cameraZ = cameraPosition.z; + Frustum frustum = new Frustum(modelViewMatrix, projectionMatrix); + frustum.prepare(cameraX, cameraY, cameraZ); + LevelRendererPortalSceneInvoker scene = (LevelRendererPortalSceneInvoker) renderer; + float renderDistance = minecraft.gameRenderer.getRenderDistance(); + BlockPos cameraBlockPos = BlockPos.containing(cameraX, cameraY, cameraZ); + boolean foggy = minecraft.level.effects().isFoggyAt(cameraBlockPos.getX(), cameraBlockPos.getY()) + || minecraft.gui.getBossOverlay().shouldCreateWorldFog(); + + try (PortalSceneRenderTrace.Phase ignored = PortalSceneRenderTrace.begin(tracePair, "sky", "camera=" + cameraPosition)) { + RenderSystem.setShaderGameTime(minecraft.level.getGameTime(), partialTick); + minecraft.getBlockEntityRenderDispatcher().prepare(minecraft.level, camera, minecraft.hitResult); + minecraft.getEntityRenderDispatcher().prepare(minecraft.level, camera, minecraft.crosshairPickEntity); + FogRenderer.setupColor(camera, partialTick, minecraft.level, minecraft.options.getEffectiveRenderDistance(), minecraft.gameRenderer.getDarkenWorldAmount(partialTick)); + FogRenderer.levelFogColor(); + FogRenderer.setupFog(camera, FogRenderer.FogMode.FOG_SKY, renderDistance, foggy, partialTick); + RenderSystem.setShader(net.minecraft.client.renderer.GameRenderer::getPositionShader); + renderer.renderSky(modelViewMatrix, projectionMatrix, partialTick, camera, foggy, + () -> FogRenderer.setupFog(camera, FogRenderer.FogMode.FOG_SKY, renderDistance, foggy, partialTick)); + } + + try (PortalSceneRenderTrace.Phase ignored = PortalSceneRenderTrace.begin(tracePair, "frustum-and-visibility", "renderDistance=" + rendererAccessDistance(renderer))) { + FogRenderer.setupFog(camera, FogRenderer.FogMode.FOG_TERRAIN, Math.max(renderDistance, 32.0F), foggy, partialTick); + scene.antarchy$setupPortalScene(camera, frustum, false, minecraft.player != null && minecraft.player.isSpectator()); + } + LevelRendererPortalViewAreaAccessor viewAreaAccessor = (LevelRendererPortalViewAreaAccessor) renderer; + ObjectArrayList fullVisibleSections = viewAreaAccessor.antarchy$getVisibleSections(); + ObjectArrayList budgetedVisibleSections = new ObjectArrayList<>(); + PortalGunPortalRendererPool.beginSceneBudget(); + int scannedVisibleSections = 0; + while (scannedVisibleSections < fullVisibleSections.size() && PortalGunPortalRendererPool.remainingSectionBudgetNanos() > 0L) { + budgetedVisibleSections.add(fullVisibleSections.get(scannedVisibleSections++)); + } + try (PortalSceneRenderTrace.Phase ignored = PortalSceneRenderTrace.begin(tracePair, "compile-visible-sections", "visibleTotal=" + fullVisibleSections.size() + " visibleScanned=" + scannedVisibleSections + " rebuildBudgetNanos=" + PortalGunPortalRendererPool.remainingSectionBudgetNanos())) { + viewAreaAccessor.antarchy$setVisibleSections(budgetedVisibleSections); + try { + scene.antarchy$compilePortalSections(camera); + } finally { + viewAreaAccessor.antarchy$setVisibleSections(fullVisibleSections); + } + } + PortalSceneRenderTrace.event(tracePair, "visible-sections-ready", "count=" + viewAreaAccessor.antarchy$getVisibleSections().size() + " scheduledRebuilds=" + PortalGunPortalRendererPool.scheduledSectionRebuilds()); + + try (PortalSceneRenderTrace.Phase ignored = PortalSceneRenderTrace.begin(tracePair, "opaque-terrain-layers", "sections=" + viewAreaAccessor.antarchy$getVisibleSections().size())) { + RenderSystem.enableDepthTest(); + RenderSystem.depthMask(true); + scene.antarchy$renderPortalSectionLayer(RenderType.solid(), cameraX, cameraY, cameraZ, modelViewMatrix, projectionMatrix); + scene.antarchy$renderPortalSectionLayer(RenderType.cutoutMipped(), cameraX, cameraY, cameraZ, modelViewMatrix, projectionMatrix); + scene.antarchy$renderPortalSectionLayer(RenderType.cutout(), cameraX, cameraY, cameraZ, modelViewMatrix, projectionMatrix); + } + + if (minecraft.level.effects().constantAmbientLight()) { + Lighting.setupNetherLevel(); + } else { + Lighting.setupLevel(); + } + + Matrix4fStack modelViewStack = RenderSystem.getModelViewStack(); + try (PortalSceneRenderTrace.Phase ignored = PortalSceneRenderTrace.begin(tracePair, "entity-scene-state", "enter-model-view")) { + modelViewStack.pushMatrix(); + modelViewStack.mul(modelViewMatrix); + RenderSystem.applyModelViewMatrix(); + } + PoseStack poseStack = new PoseStack(); + MultiBufferSource.BufferSource buffers = minecraft.renderBuffers().bufferSource(); + try { + int entityCount = 0; + try (PortalSceneRenderTrace.Phase ignored = PortalSceneRenderTrace.begin(tracePair, "entities", "dispatch-start")) { + for (Entity entity : minecraft.level.entitiesForRendering()) { + if (!minecraft.getEntityRenderDispatcher().shouldRender(entity, frustum, cameraX, cameraY, cameraZ) + && !entity.hasIndirectPassenger(minecraft.player)) { + continue; + } + if (entity == camera.getEntity() && !camera.isDetached() + && (!(entity instanceof LivingEntity living) || !living.isSleeping())) { + continue; + } + if (entity instanceof Player player && player == minecraft.player && !player.isSpectator() && entity != camera.getEntity()) { + continue; + } + scene.antarchy$renderPortalEntity(entity, cameraX, cameraY, cameraZ, partialTick, poseStack, buffers); + entityCount++; + } + } + buffers.endBatch(); + PortalSceneRenderTrace.event(tracePair, "entities-dispatched", "count=" + entityCount); + + int blockEntityCount = 0; + try (PortalSceneRenderTrace.Phase ignored = PortalSceneRenderTrace.begin(tracePair, "block-entities", "visibleSections=" + viewAreaAccessor.antarchy$getVisibleSections().size())) { + for (var section : viewAreaAccessor.antarchy$getVisibleSections()) { + for (BlockEntity blockEntity : section.getCompiled().getRenderableBlockEntities()) { + renderBlockEntity(minecraft, blockEntity, cameraX, cameraY, cameraZ, partialTick, poseStack, buffers); + blockEntityCount++; + } + } + synchronized (viewAreaAccessor.antarchy$getGlobalBlockEntities()) { + for (BlockEntity blockEntity : viewAreaAccessor.antarchy$getGlobalBlockEntities()) { + if (frustum.isVisible(new AABB(blockEntity.getBlockPos()).inflate(1.0D))) { + renderBlockEntity(minecraft, blockEntity, cameraX, cameraY, cameraZ, partialTick, poseStack, buffers); + blockEntityCount++; + } + } + } + } + buffers.endBatch(); + PortalSceneRenderTrace.event(tracePair, "block-entities-dispatched", "count=" + blockEntityCount); + + try (PortalSceneRenderTrace.Phase ignored = PortalSceneRenderTrace.begin(tracePair, "translucent-and-particles", "rendering")) { + minecraft.particleEngine.render(minecraft.gameRenderer.lightTexture(), camera, partialTick, frustum, type -> !type.isTranslucent()); + scene.antarchy$renderPortalSectionLayer(RenderType.translucent(), cameraX, cameraY, cameraZ, modelViewMatrix, projectionMatrix); + buffers.endBatch(RenderType.lines()); + buffers.endBatch(); + scene.antarchy$renderPortalSectionLayer(RenderType.tripwire(), cameraX, cameraY, cameraZ, modelViewMatrix, projectionMatrix); + minecraft.particleEngine.render(minecraft.gameRenderer.lightTexture(), camera, partialTick, frustum, ParticleRenderType::isTranslucent); + } + + try (PortalSceneRenderTrace.Phase ignored = PortalSceneRenderTrace.begin(tracePair, "clouds", minecraft.options.getCloudsType().toString())) { + if (minecraft.options.getCloudsType() != CloudStatus.OFF) { + renderer.renderClouds(poseStack, modelViewMatrix, projectionMatrix, partialTick, cameraX, cameraY, cameraZ); + } + } + + try (PortalSceneRenderTrace.Phase ignored = PortalSceneRenderTrace.begin(tracePair, "weather-and-border", "rendering")) { + RenderSystem.depthMask(false); + scene.antarchy$renderPortalWeather(minecraft.gameRenderer.lightTexture(), partialTick, cameraX, cameraY, cameraZ); + scene.antarchy$renderPortalWorldBorder(camera); + } + } finally { + try { + buffers.endBatch(); + } finally { + modelViewStack.popMatrix(); + RenderSystem.applyModelViewMatrix(); + RenderSystem.depthMask(true); + RenderSystem.enableDepthTest(); + RenderSystem.enableCull(); + RenderSystem.disableBlend(); + } + } + } + + private static int rendererAccessDistance(LevelRenderer renderer) { + return ((LevelRendererPortalViewAreaAccessor) renderer).antarchy$getViewArea().getViewDistance(); + } + + private static void renderBlockEntity(Minecraft minecraft, BlockEntity blockEntity, double cameraX, double cameraY, double cameraZ, + float partialTick, PoseStack poseStack, MultiBufferSource.BufferSource buffers) { + if (!blockEntity.hasLevel() || !blockEntity.getType().isValid(blockEntity.getBlockState())) { + return; + } + BlockPos pos = blockEntity.getBlockPos(); + poseStack.pushPose(); + poseStack.translate(pos.getX() - cameraX, pos.getY() - cameraY, pos.getZ() - cameraZ); + minecraft.getBlockEntityRenderDispatcher().render(blockEntity, partialTick, poseStack, buffers); + poseStack.popPose(); + } +} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/PortalSceneRenderTrace.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/PortalSceneRenderTrace.java new file mode 100644 index 00000000..5be56e6b --- /dev/null +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/PortalSceneRenderTrace.java @@ -0,0 +1,82 @@ +package com.craisinlord.antarchy.content.client.renderer; + +import com.craisinlord.antarchy.Antarchy; +import java.util.HashSet; +import java.util.Set; + +public final class PortalSceneRenderTrace { + private static final Set LOGGED_PHASES = new HashSet<>(); + private static long generation; + private static String armedKey = ""; + + private PortalSceneRenderTrace() { + } + + public static synchronized void arm(String key) { + if (!armedKey.equals(key)) { + armedKey = key; + generation++; + LOGGED_PHASES.clear(); + } + } + + public static synchronized void reset() { + armedKey = ""; + LOGGED_PHASES.clear(); + generation++; + } + + public static Phase begin(String pair, String phase, String detail) { + long currentGeneration; + boolean shouldLog; + synchronized (PortalSceneRenderTrace.class) { + currentGeneration = generation; + shouldLog = LOGGED_PHASES.add(currentGeneration + ":" + pair + ":" + phase); + } + if (shouldLog) { + Antarchy.LOGGER.info("Portal scene trace begin generation={} pair={} phase={} detail={}", currentGeneration, pair, phase, detail); + } + return new Phase(currentGeneration, pair, phase, shouldLog, System.nanoTime()); + } + + public static void event(String pair, String event, String detail) { + long currentGeneration; + boolean shouldLog; + synchronized (PortalSceneRenderTrace.class) { + currentGeneration = generation; + shouldLog = LOGGED_PHASES.add(currentGeneration + ":" + pair + ":event:" + event); + } + if (shouldLog) { + Antarchy.LOGGER.info("Portal scene trace event generation={} pair={} event={} detail={}", currentGeneration, pair, event, detail); + } + } + + public static final class Phase implements AutoCloseable { + private final long generation; + private final String pair; + private final String phase; + private final boolean logCompletion; + private final long startNanos; + private boolean closed; + + private Phase(long generation, String pair, String phase, boolean logCompletion, long startNanos) { + this.generation = generation; + this.pair = pair; + this.phase = phase; + this.logCompletion = logCompletion; + this.startNanos = startNanos; + } + + @Override + public void close() { + if (this.closed) { + return; + } + this.closed = true; + if (this.logCompletion) { + Antarchy.LOGGER.info("Portal scene trace complete generation={} pair={} phase={} durationMs={}", + this.generation, this.pair, this.phase, (System.nanoTime() - this.startNanos) / 1_000_000.0D); + } + } + } +} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/PortalStencilTarget.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/PortalStencilTarget.java new file mode 100644 index 00000000..fe6562d7 --- /dev/null +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/PortalStencilTarget.java @@ -0,0 +1,196 @@ +package com.craisinlord.antarchy.content.client.renderer; + +import com.mojang.blaze3d.pipeline.RenderTarget; +import com.mojang.blaze3d.systems.RenderSystem; +import java.util.Collections; +import java.util.IdentityHashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import org.lwjgl.opengl.GL11; +import org.lwjgl.opengl.GL30; +import com.mojang.blaze3d.pipeline.TextureTarget; + +public final class PortalStencilTarget { + private static final Map ATTACHMENTS = Collections.synchronizedMap(new IdentityHashMap<>()); + private static final Set DIRECT_TARGETS = Collections.newSetFromMap(new IdentityHashMap<>()); + + private PortalStencilTarget() { + } + + public static void prepareMainTarget(RenderTarget target) { + try { + java.lang.reflect.Method enabled = target.getClass().getMethod("isStencilEnabled"); + if (!Boolean.TRUE.equals(enabled.invoke(target))) { + target.getClass().getMethod("enableStencil").invoke(target); + } + } catch (ReflectiveOperationException ignored) { + } + target.bindWrite(true); + } + + public static Scope beginDirect(RenderTarget target, TextureTarget depthBackup, Runnable drawApertureMask) { + target.bindWrite(true); + if (GL11.glGetInteger(GL11.GL_STENCIL_BITS) <= 0) { + throw new IllegalStateException("Main render target has no stencil attachment"); + } + depthBackup.copyDepthFrom(target); + DIRECT_TARGETS.add(target); + target.bindWrite(true); + RenderSystem.disableScissor(); + RenderSystem.stencilMask(0xFF); + RenderSystem.clearStencil(0); + GL11.glClear(GL11.GL_STENCIL_BUFFER_BIT); + RenderSystem.stencilFunc(GL11.GL_ALWAYS, 1, 0xFF); + RenderSystem.stencilOp(GL11.GL_KEEP, GL11.GL_KEEP, GL11.GL_REPLACE); + RenderSystem.colorMask(false, false, false, false); + RenderSystem.depthMask(false); + RenderSystem.disableDepthTest(); + GL11.glEnable(GL11.GL_STENCIL_TEST); + try { + Objects.requireNonNull(drawApertureMask).run(); + } catch (RuntimeException | Error exception) { + resetState(); + target.copyDepthFrom(depthBackup); + DIRECT_TARGETS.remove(target); + throw exception; + } + RenderSystem.colorMask(true, true, true, true); + RenderSystem.depthMask(true); + RenderSystem.stencilMask(0x00); + RenderSystem.stencilFunc(GL11.GL_EQUAL, 1, 0xFF); + RenderSystem.stencilOp(GL11.GL_KEEP, GL11.GL_KEEP, GL11.GL_KEEP); + GL11.glClear(GL11.GL_DEPTH_BUFFER_BIT); + RenderSystem.enableDepthTest(); + return new Scope(target, depthBackup); + } + + public static Scope begin(RenderTarget target, Runnable drawApertureMask) { + ensureAttachment(target); + target.bindWrite(true); + RenderSystem.disableScissor(); + RenderSystem.stencilMask(0xFF); + RenderSystem.clearStencil(0); + GL11.glClear(GL11.GL_STENCIL_BUFFER_BIT); + RenderSystem.stencilFunc(GL11.GL_ALWAYS, 1, 0xFF); + RenderSystem.stencilOp(GL11.GL_KEEP, GL11.GL_KEEP, GL11.GL_REPLACE); + RenderSystem.colorMask(false, false, false, false); + RenderSystem.depthMask(false); + RenderSystem.disableDepthTest(); + GL11.glEnable(GL11.GL_STENCIL_TEST); + try { + Objects.requireNonNull(drawApertureMask).run(); + } catch (RuntimeException | Error exception) { + resetState(); + throw exception; + } + RenderSystem.colorMask(true, true, true, true); + RenderSystem.depthMask(true); + RenderSystem.stencilMask(0x00); + RenderSystem.stencilFunc(GL11.GL_EQUAL, 1, 0xFF); + RenderSystem.stencilOp(GL11.GL_KEEP, GL11.GL_KEEP, GL11.GL_KEEP); + GL11.glClear(GL11.GL_DEPTH_BUFFER_BIT); + RenderSystem.enableDepthTest(); + return new Scope(target); + } + + public static void activate(RenderTarget target) { + if (!ATTACHMENTS.containsKey(target) && !DIRECT_TARGETS.contains(target)) { + return; + } + target.bindWrite(true); + GL11.glEnable(GL11.GL_STENCIL_TEST); + RenderSystem.stencilMask(0x00); + RenderSystem.stencilFunc(GL11.GL_EQUAL, 1, 0xFF); + RenderSystem.stencilOp(GL11.GL_KEEP, GL11.GL_KEEP, GL11.GL_KEEP); + } + + public static void release(RenderTarget target) { + DIRECT_TARGETS.remove(target); + Attachment attachment = ATTACHMENTS.remove(target); + if (attachment != null) { + GL30.glDeleteRenderbuffers(attachment.renderbuffer()); + } + } + + public static void clear() { + for (Attachment attachment : ATTACHMENTS.values()) { + GL30.glDeleteRenderbuffers(attachment.renderbuffer()); + } + ATTACHMENTS.clear(); + DIRECT_TARGETS.clear(); + } + + private static void ensureAttachment(RenderTarget target) { + Attachment attachment = ATTACHMENTS.get(target); + if (attachment != null + && attachment.framebuffer() == target.frameBufferId + && attachment.width() == target.width + && attachment.height() == target.height) { + return; + } + if (attachment != null) { + GL30.glDeleteRenderbuffers(attachment.renderbuffer()); + } + int previousFramebuffer = GL11.glGetInteger(GL30.GL_FRAMEBUFFER_BINDING); + int previousRenderbuffer = GL11.glGetInteger(GL30.GL_RENDERBUFFER_BINDING); + int renderbuffer = GL30.glGenRenderbuffers(); + try { + GL30.glBindFramebuffer(GL30.GL_FRAMEBUFFER, target.frameBufferId); + GL30.glBindRenderbuffer(GL30.GL_RENDERBUFFER, renderbuffer); + GL30.glRenderbufferStorage(GL30.GL_RENDERBUFFER, GL30.GL_DEPTH24_STENCIL8, target.width, target.height); + GL30.glFramebufferRenderbuffer(GL30.GL_FRAMEBUFFER, GL30.GL_DEPTH_STENCIL_ATTACHMENT, GL30.GL_RENDERBUFFER, renderbuffer); + int status = GL30.glCheckFramebufferStatus(GL30.GL_FRAMEBUFFER); + if (status != GL30.GL_FRAMEBUFFER_COMPLETE) { + GL30.glDeleteRenderbuffers(renderbuffer); + throw new IllegalStateException("Portal view framebuffer is incomplete with packed depth/stencil attachment: 0x" + Integer.toHexString(status)); + } + ATTACHMENTS.put(target, new Attachment(target.frameBufferId, renderbuffer, target.width, target.height)); + } finally { + GL30.glBindRenderbuffer(GL30.GL_RENDERBUFFER, previousRenderbuffer); + GL30.glBindFramebuffer(GL30.GL_FRAMEBUFFER, previousFramebuffer); + } + } + + private static void resetState() { + GL11.glDisable(GL11.GL_STENCIL_TEST); + RenderSystem.stencilMask(0xFF); + RenderSystem.stencilFunc(GL11.GL_ALWAYS, 0, 0xFF); + RenderSystem.stencilOp(GL11.GL_KEEP, GL11.GL_KEEP, GL11.GL_KEEP); + RenderSystem.colorMask(true, true, true, true); + RenderSystem.depthMask(true); + RenderSystem.enableDepthTest(); + } + + private record Attachment(int framebuffer, int renderbuffer, int width, int height) { + } + + public static final class Scope implements AutoCloseable { + private boolean closed; + private final RenderTarget target; + private final TextureTarget depthBackup; + + private Scope(RenderTarget target) { + this(target, null); + } + + private Scope(RenderTarget target, TextureTarget depthBackup) { + this.target = Objects.requireNonNull(target); + this.depthBackup = depthBackup; + } + + @Override + public void close() { + if (closed) { + return; + } + closed = true; + if (this.depthBackup != null) { + this.target.copyDepthFrom(this.depthBackup); + this.target.bindWrite(true); + DIRECT_TARGETS.remove(this.target); + } + resetState(); + } + } +} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/PortalViewSectionMarker.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/PortalViewSectionMarker.java new file mode 100644 index 00000000..06eaaaa3 --- /dev/null +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/PortalViewSectionMarker.java @@ -0,0 +1,7 @@ +package com.craisinlord.antarchy.content.client.renderer; + +public interface PortalViewSectionMarker { + void antarchy$markPortalViewSection(); + + boolean antarchy$isPortalViewSection(); +} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/SodiumCompat.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/SodiumCompat.java new file mode 100644 index 00000000..cd6cc797 --- /dev/null +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/SodiumCompat.java @@ -0,0 +1,117 @@ +package com.craisinlord.antarchy.content.client.renderer; + +import com.craisinlord.antarchy.Antarchy; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayDeque; +import java.util.Deque; +import net.minecraft.client.Camera; + +public final class SodiumCompat { + private static final String SODIUM_RENDERER_CLASS = "net.caffeinemc.mods.sodium.client.render.SodiumWorldRenderer"; + private static final ThreadLocal RENDER_STATE = ThreadLocal.withInitial(RenderState::new); + private static boolean restoreWarningLogged; + + private SodiumCompat() { + } + + public static boolean isLoaded() { + String resource = SODIUM_RENDERER_CLASS.replace('.', '/') + ".class"; + ClassLoader contextLoader = Thread.currentThread().getContextClassLoader(); + if (contextLoader != null && contextLoader.getResource(resource) != null) { + return true; + } + ClassLoader ownLoader = SodiumCompat.class.getClassLoader(); + return ownLoader != null && ownLoader.getResource(resource) != null; + } + + public static void captureTerrainSetup(Camera camera, Object viewport, boolean spectator, boolean updateChunks) { + RENDER_STATE.get().activeSetup = new TerrainSetup(camera, viewport, spectator, updateChunks); + } + + public static TerrainScope beginPortalView(Camera portalCamera) { + RenderState state = RENDER_STATE.get(); + TerrainSetup previousSetup = state.activeSetup; + state.previousSetups.push(new PreviousSetup(previousSetup)); + if (previousSetup != null) { + restoreTerrainSetup(previousSetup.withCamera(portalCamera), state); + } + return () -> { + PreviousSetup previous = state.previousSetups.pop(); + if (previous.setup() == null) { + state.activeSetup = null; + return; + } + restoreTerrainSetup(previous.setup(), state); + }; + } + + public static void clear() { + RENDER_STATE.remove(); + } + + private static void restoreTerrainSetup(TerrainSetup setup, RenderState state) { + try { + ClassLoader contextLoader = Thread.currentThread().getContextClassLoader(); + Class rendererClass = Class.forName(SODIUM_RENDERER_CLASS, false, + contextLoader == null ? SodiumCompat.class.getClassLoader() : contextLoader); + Object renderer = rendererClass.getMethod("instanceNullable").invoke(null); + if (renderer == null) { + return; + } + Method terrainSetup = null; + for (Method method : rendererClass.getMethods()) { + Class[] parameters = method.getParameterTypes(); + if (method.getName().equals("setupTerrain") + && parameters.length == 4 + && parameters[0].isInstance(setup.camera()) + && parameters[1].isInstance(setup.viewport()) + && parameters[2] == boolean.class + && parameters[3] == boolean.class) { + terrainSetup = method; + break; + } + } + if (terrainSetup == null) { + logRestoreFailure("Sodium terrain setup method was not found", null); + state.activeSetup = setup; + return; + } + terrainSetup.invoke(renderer, setup.camera(), setup.viewport(), setup.spectator(), setup.updateChunks()); + } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException | LinkageError exception) { + logRestoreFailure("Could not restore Sodium terrain after a portal view", exception); + state.activeSetup = setup; + } + } + + private static void logRestoreFailure(String message, Throwable exception) { + if (!restoreWarningLogged) { + restoreWarningLogged = true; + if (exception == null) { + Antarchy.LOGGER.warn(message); + } else { + Antarchy.LOGGER.warn(message, exception); + } + } + } + + @FunctionalInterface + public interface TerrainScope extends AutoCloseable { + @Override + void close(); + } + + private static final class RenderState { + private final Deque previousSetups = new ArrayDeque<>(); + private TerrainSetup activeSetup; + } + + private record PreviousSetup(TerrainSetup setup) { + } + + private record TerrainSetup(Camera camera, Object viewport, boolean spectator, boolean updateChunks) { + private TerrainSetup withCamera(Camera camera) { + return new TerrainSetup(camera, this.viewport, this.spectator, this.updateChunks); + } + } +} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/computer/AntarchyArchiveLocators.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/computer/AntarchyArchiveLocators.java index d2f8fa50..64feaaae 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/computer/AntarchyArchiveLocators.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/computer/AntarchyArchiveLocators.java @@ -2,11 +2,13 @@ package com.craisinlord.antarchy.content.computer; import com.craisinlord.antarchy.Antarchy; import com.craisinlord.antarchy.content.command.QueenLocateCommand; +import com.craisinlord.antarchy.content.entity.brutalfly.BrutalflyEntity; import com.craisinlord.antarchy.content.worldgen.elythia.KingsTreeGrid; import com.craisinlord.antos.api.archive.ArchiveLocatorRegistry; import net.minecraft.core.BlockPos; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.level.levelgen.Heightmap; +import net.minecraft.world.phys.AABB; /** Archive coordinate lookups for royals that live on grids rather than in vanilla structures. */ public final class AntarchyArchiveLocators { @@ -24,6 +26,14 @@ public final class AntarchyArchiveLocators { Heightmap.Types.WORLD_SURFACE_WG, level, level.getChunkSource().randomState()); return new BlockPos(center.getX(), surfaceY, center.getZ()); }); + ArchiveLocatorRegistry.register(id("brutalfly"), (level, origin) -> { + AABB searchArea = AABB.ofSize(origin.getCenter(), 1_024.0D, 1_024.0D, 1_024.0D); + return level.getEntitiesOfClass(BrutalflyEntity.class, searchArea, BrutalflyEntity::isAlive) + .stream() + .min(java.util.Comparator.comparingDouble(entity -> entity.distanceToSqr(origin.getCenter()))) + .map(entity -> entity.blockPosition()) + .orElse(null); + }); } private static ResourceLocation id(String path) { diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/creative/CreativeTabContents.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/creative/CreativeTabContents.java index d25db275..80ff3128 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/creative/CreativeTabContents.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/creative/CreativeTabContents.java @@ -4,6 +4,7 @@ import com.craisinlord.antarchy.content.AntarchyObjects; import com.craisinlord.antarchy.content.entity.glimmer.GlimmerVariant; import com.craisinlord.antarchy.content.item.GiantFryingPanToolHelper; import com.craisinlord.antarchy.content.item.GlimmerBottleItem; +import com.craisinlord.antarchy.content.item.PortalGunItem; import com.craisinlord.antarchy.content.item.royal.RoyalGearHelper; import com.craisinlord.antarchy.content.item.ultimate.UltimateGearHelper; import java.util.HashMap; @@ -425,7 +426,7 @@ public final class CreativeTabContents { entry("shrink_ray", "combat"), entry("growth_ray", "combat"), entry("gravity_gun", "combat"), - // entry("portal_gun", "combat"), // deferred — out of scope for 2.0 + entry("portal_gun", "combat"), entry("eye_of_the_storm", "combat"), entry("water_cannon", "combat"), entry("attitude_adjuster", "combat"), @@ -640,6 +641,14 @@ public final class CreativeTabContents { } continue; } + if ("portal_gun".equals(entry.path())) { + Item portalGun = item(entry.path()); + output.accept(portalGun); + output.accept(PortalGunItem.createGlobalVariant(portalGun, "Chell")); + output.accept(PortalGunItem.createGlobalVariant(portalGun, "Atlas")); + output.accept(PortalGunItem.createGlobalVariant(portalGun, "P-body")); + continue; + } output.accept(item(entry.path())); } for (String potionPath : ANTARCHY_POTION_FAMILIES) { @@ -669,6 +678,14 @@ public final class CreativeTabContents { output.insertAfter(vanillaItem(entry.insertAfterPath()), item(entry.path())); continue; } + if ("portal_gun".equals(entry.path())) { + Item portalGun = item(entry.path()); + output.accept(portalGun); + output.accept(PortalGunItem.createGlobalVariant(portalGun, "Chell")); + output.accept(PortalGunItem.createGlobalVariant(portalGun, "Atlas")); + output.accept(PortalGunItem.createGlobalVariant(portalGun, "P-body")); + continue; + } if ("combat".equals(tabPath)) { if (isUltimateArmor(entry.path())) { output.accept(UltimateGearHelper.createUltimateArmorStack(item(entry.path()), holders)); diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/gravity/ScgunsProjectileAccess.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/gravity/ScgunsProjectileAccess.java new file mode 100644 index 00000000..6b640e8a --- /dev/null +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/gravity/ScgunsProjectileAccess.java @@ -0,0 +1,10 @@ +package com.craisinlord.antarchy.content.gravity; + +import net.minecraft.world.entity.LivingEntity; +import org.jetbrains.annotations.Nullable; + +/** Exposes Scorched Guns' base ProjectileEntity fields to mixins targeting its subclasses. */ +public interface ScgunsProjectileAccess { + @Nullable LivingEntity antarchy$getScgunsShooter(); + double antarchy$getScgunsModifiedGravity(); +} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/item/BigBerthaItem.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/item/BigBerthaItem.java index 8f4ba348..22cb89a9 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/item/BigBerthaItem.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/item/BigBerthaItem.java @@ -59,6 +59,8 @@ public class BigBerthaItem extends SwordItem implements GeoItem { private static final ResourceLocation TEXTURE_LOCATION = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/item/big_bertha/big_bertha.png"); private static final ResourceLocation ANIMATION_LOCATION = ResourceLocation.fromNamespaceAndPath("antarchy", "animations/static_item.animation.json"); private static final String MODE_TAG = "antarchy.big_bertha_mode"; + public static final int MODE_TREMORSAURUS = 8; + public static final int MODE_FORSAKEN = 9; private static final String SPIN_TICKS_TAG = "antarchy.big_bertha_spin_ticks"; private static final String SPIN_DIRECTION_X_TAG = "antarchy.big_bertha_spin_direction_x"; private static final String SPIN_DIRECTION_Z_TAG = "antarchy.big_bertha_spin_direction_z"; @@ -161,7 +163,7 @@ public class BigBerthaItem extends SwordItem implements GeoItem { case NIGHTMARE -> applyNightmareHit(player, target, serverLevel); case KRAKEN -> applyKrakenHit(player, target, serverLevel); case HERCULES -> applyHerculesHit(player, target, serverLevel); - case MOLEVORE, TORETERROR -> { + case MOLEVORE, TORETERROR, TREMORSAURUS, FORSAKEN -> { } } @@ -389,6 +391,10 @@ public class BigBerthaItem extends SwordItem implements GeoItem { } } + public static int getModeId(ItemStack stack) { + return getMode(stack).id; + } + public boolean tryCycleModeFromInput(Level level, Player player, ItemStack stack) { if (player.getMainHandItem() != stack) { return false; @@ -650,6 +656,8 @@ public class BigBerthaItem extends SwordItem implements GeoItem { MOLEVORE(3, "tooltip.antarchy.big_bertha.mode.molevore", "message.antarchy.big_bertha.mode.molevore"), LUCID(4, "tooltip.antarchy.big_bertha.mode.lucid", "message.antarchy.big_bertha.mode.lucid"), TORETERROR(5, "tooltip.antarchy.big_bertha.mode.toreterror", "message.antarchy.big_bertha.mode.toreterror"), + TREMORSAURUS(MODE_TREMORSAURUS, "tooltip.antarchy.big_bertha.mode.tremorsaurus", "message.antarchy.big_bertha.mode.tremorsaurus"), + FORSAKEN(MODE_FORSAKEN, "tooltip.antarchy.big_bertha.mode.forsaken", "message.antarchy.big_bertha.mode.forsaken"), HERCULES(6, "tooltip.antarchy.big_bertha.mode.hercules", "message.antarchy.big_bertha.mode.hercules"); private final int id; @@ -671,7 +679,9 @@ public class BigBerthaItem extends SwordItem implements GeoItem { case KRAKEN -> HERCULES; case HERCULES -> MOLEVORE; case MOLEVORE -> TORETERROR; - case TORETERROR -> NONE; + case TORETERROR -> TREMORSAURUS; + case TREMORSAURUS -> FORSAKEN; + case FORSAKEN -> NONE; }; } diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/item/MinersDreamCaveGenerator.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/item/MinersDreamCaveGenerator.java deleted file mode 100644 index 4aa42bac..00000000 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/item/MinersDreamCaveGenerator.java +++ /dev/null @@ -1,173 +0,0 @@ -package com.craisinlord.antarchy.content.item; - -import com.craisinlord.antarchy.content.AntarchyTags; -import it.unimi.dsi.fastutil.longs.LongOpenHashSet; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.util.Mth; -import net.minecraft.util.RandomSource; -import net.minecraft.world.level.block.state.BlockState; - -public final class MinersDreamCaveGenerator { - private static final double MIN_HORIZONTAL_RADIUS = 2.0D; - private static final double MAX_HORIZONTAL_RADIUS = 5.0D; - private static final double MIN_VERTICAL_RADIUS = 1.5D; - private static final double MAX_VERTICAL_RADIUS = 4.0D; - private static final double MAX_VERTICAL_DEVIATION = 8.0D; - private static final double SOFT_VERTICAL_DEVIATION = 4.0D; - private static final int WORLD_MARGIN = 3; - - private MinersDreamCaveGenerator() { - } - - public record CaveNode(BlockPos center, double horizontalRadius, double verticalRadius) { - } - - public record CaveGenerationResult(List nodes, Set torchCheckpoints) { - } - - public static CaveGenerationResult generate(BlockPos start, Direction initialDirection, int length, - int torchSpacing, int minBuildHeight, int maxBuildHeight, RandomSource random) { - List nodes = new ArrayList<>(length); - Set torchCheckpoints = new HashSet<>(); - - double x = start.getX() + 0.5D; - double y = start.getY() + 0.5D; - double z = start.getZ() + 0.5D; - double startY = y; - - double headingX = initialDirection.getStepX(); - double headingZ = initialDirection.getStepZ(); - if (headingX == 0.0D && headingZ == 0.0D) { - headingX = 1.0D; - } - - double verticalVelocity = 0.0D; - double horizontalRadius = 3.0D; - double verticalRadius = 2.0D; - int minY = minBuildHeight + WORLD_MARGIN; - int maxY = maxBuildHeight - WORLD_MARGIN; - - int spacing = Math.max(4, torchSpacing); - int traveledSinceTorch = 0; - int nextTorchDistance = spacing + random.nextInt(5) - 2; - - for (int i = 0; i < length; i++) { - BlockPos center = BlockPos.containing(x, Mth.clamp(y, minY, maxY), z); - nodes.add(new CaveNode(center, horizontalRadius, verticalRadius)); - - traveledSinceTorch++; - if (traveledSinceTorch >= Math.max(1, nextTorchDistance)) { - torchCheckpoints.add(i); - traveledSinceTorch = 0; - nextTorchDistance = spacing + random.nextInt(5) - 2; - } - - double turnDegrees = random.nextGaussian() * 8.0D; - if (random.nextFloat() < 0.1F) { - turnDegrees = random.nextGaussian() * 15.0D; - } - turnDegrees = Mth.clamp(turnDegrees, -25.0D, 25.0D); - double turnRadians = Math.toRadians(turnDegrees); - double cos = Math.cos(turnRadians); - double sin = Math.sin(turnRadians); - double newHeadingX = headingX * cos - headingZ * sin; - double newHeadingZ = headingX * sin + headingZ * cos; - double headingLength = Math.sqrt(newHeadingX * newHeadingX + newHeadingZ * newHeadingZ); - headingX = newHeadingX / headingLength; - headingZ = newHeadingZ / headingLength; - - verticalVelocity += random.nextGaussian() * 0.15D; - double deviation = y - startY; - verticalVelocity -= deviation * 0.06D; - if (deviation > SOFT_VERTICAL_DEVIATION) { - verticalVelocity -= 0.08D; - } else if (deviation < -SOFT_VERTICAL_DEVIATION) { - verticalVelocity += 0.08D; - } - verticalVelocity = Mth.clamp(verticalVelocity, -0.6D, 0.6D); - - x += headingX; - z += headingZ; - y = Mth.clamp(y + verticalVelocity, startY - MAX_VERTICAL_DEVIATION, startY + MAX_VERTICAL_DEVIATION); - - horizontalRadius = Mth.clamp(horizontalRadius + random.nextGaussian() * 0.15D, MIN_HORIZONTAL_RADIUS, MAX_HORIZONTAL_RADIUS); - verticalRadius = Mth.clamp(verticalRadius + random.nextGaussian() * 0.12D, MIN_VERTICAL_RADIUS, MAX_VERTICAL_RADIUS); - if (random.nextFloat() < 0.05F) { - horizontalRadius = Mth.clamp(horizontalRadius + 0.6D, MIN_HORIZONTAL_RADIUS, MAX_HORIZONTAL_RADIUS); - verticalRadius = Mth.clamp(verticalRadius + 0.5D, MIN_VERTICAL_RADIUS, MAX_VERTICAL_RADIUS); - } - } - - return new CaveGenerationResult(nodes, torchCheckpoints); - } - - public static void collectNodeCandidates(ServerLevel level, CaveNode node, LongOpenHashSet visited, RandomSource random, List out) { - BlockPos center = node.center(); - int hr = Mth.ceil(node.horizontalRadius()); - double upExtent = node.verticalRadius() * 1.25D; - double downExtent = node.verticalRadius() * 0.65D; - int upR = Mth.ceil(upExtent); - int downR = Mth.ceil(downExtent); - int minBuildHeight = level.getMinBuildHeight(); - int maxBuildHeight = level.getMaxBuildHeight(); - - for (int dy = -downR; dy <= upR; dy++) { - double vExtent = dy >= 0 ? upExtent : downExtent; - if (vExtent <= 0.0D) { - continue; - } - double vTerm = (dy * dy) / (vExtent * vExtent); - if (vTerm > 1.05D) { - continue; - } - - for (int dx = -hr; dx <= hr; dx++) { - double hxTerm = (dx * dx) / (node.horizontalRadius() * node.horizontalRadius()); - for (int dz = -hr; dz <= hr; dz++) { - double hzTerm = (dz * dz) / (node.horizontalRadius() * node.horizontalRadius()); - double distance = hxTerm + vTerm + hzTerm; - if (distance > 1.0D) { - continue; - } - if (distance > 0.7D) { - double edgeChance = (1.0D - distance) / 0.3D * 0.85D; - if (random.nextFloat() > edgeChance) { - continue; - } - } - - BlockPos pos = center.offset(dx, dy, dz); - if (pos.getY() < minBuildHeight || pos.getY() >= maxBuildHeight) { - continue; - } - - long packed = pos.asLong(); - if (visited.contains(packed)) { - continue; - } - - BlockState state = level.getBlockState(pos); - if (!state.is(AntarchyTags.Blocks.MINERS_DREAM_CARVABLE)) { - continue; - } - if (level.getBlockEntity(pos) != null) { - continue; - } - - visited.add(packed); - out.add(pos); - } - } - } - } - - public static boolean isChunkLoaded(ServerLevel level, BlockPos pos) { - return level.isLoaded(pos); - } -} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/item/MinersDreamExcavation.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/item/MinersDreamExcavation.java deleted file mode 100644 index 2042bb45..00000000 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/item/MinersDreamExcavation.java +++ /dev/null @@ -1,152 +0,0 @@ -package com.craisinlord.antarchy.content.item; - -import it.unimi.dsi.fastutil.longs.LongOpenHashSet; -import java.util.ArrayList; -import java.util.List; -import java.util.Set; -import java.util.UUID; -import net.minecraft.core.BlockPos; -import net.minecraft.core.particles.BlockParticleOption; -import net.minecraft.core.particles.DustParticleOptions; -import net.minecraft.core.particles.ParticleTypes; -import net.minecraft.resources.ResourceKey; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.sounds.SoundEvents; -import net.minecraft.sounds.SoundSource; -import net.minecraft.util.RandomSource; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.block.Blocks; -import net.minecraft.world.level.block.state.BlockState; -import org.joml.Vector3f; - -public final class MinersDreamExcavation { - private static final Vector3f FLYTRAP_GOO_COLOR = new Vector3f(0.35F, 0.85F, 0.4F); - private static final int SLICES_BETWEEN_RUMBLE = 4; - - private final UUID owner; - private final ResourceKey dimension; - private final List nodes; - private final Set torchCheckpoints; - private final LongOpenHashSet visited = new LongOpenHashSet(); - - private int nodeIndex = 0; - private List currentNodePositions = null; - private int positionCursor = 0; - private int slicesSinceRumble = 0; - private boolean finished = false; - - public MinersDreamExcavation(UUID owner, ResourceKey dimension, List nodes, Set torchCheckpoints) { - this.owner = owner; - this.dimension = dimension; - this.nodes = nodes; - this.torchCheckpoints = torchCheckpoints; - } - - public UUID owner() { - return owner; - } - - public ResourceKey dimension() { - return dimension; - } - - public boolean isFinished() { - return finished; - } - - public void tick(ServerLevel level, int blockBudget) { - RandomSource random = level.getRandom(); - int remaining = blockBudget; - - while (remaining > 0 && !finished) { - if (currentNodePositions == null) { - if (nodeIndex >= nodes.size()) { - finished = true; - playCompletionSound(level); - return; - } - - MinersDreamCaveGenerator.CaveNode node = nodes.get(nodeIndex); - if (!MinersDreamCaveGenerator.isChunkLoaded(level, node.center())) { - advanceNode(level, node); - continue; - } - - currentNodePositions = new ArrayList<>(); - MinersDreamCaveGenerator.collectNodeCandidates(level, node, visited, random, currentNodePositions); - positionCursor = 0; - } - - if (currentNodePositions.isEmpty()) { - MinersDreamCaveGenerator.CaveNode node = nodes.get(nodeIndex); - advanceNode(level, node); - continue; - } - - int endIndex = Math.min(currentNodePositions.size(), positionCursor + remaining); - for (; positionCursor < endIndex; positionCursor++) { - BlockPos pos = currentNodePositions.get(positionCursor); - BlockState removedState = level.getBlockState(pos); - if (removedState.isAir()) { - continue; - } - level.setBlock(pos, Blocks.AIR.defaultBlockState(), 3); - remaining--; - if (random.nextFloat() < 0.12F) { - spawnBreakParticles(level, pos, removedState, random); - } - } - - if (positionCursor >= currentNodePositions.size()) { - MinersDreamCaveGenerator.CaveNode node = nodes.get(nodeIndex); - advanceNode(level, node); - } - } - } - - private void advanceNode(ServerLevel level, MinersDreamCaveGenerator.CaveNode node) { - RandomSource random = level.getRandom(); - spawnFrontDust(level, node, random); - slicesSinceRumble++; - if (slicesSinceRumble >= SLICES_BETWEEN_RUMBLE) { - slicesSinceRumble = 0; - playRumble(level, node); - } - - if (torchCheckpoints.contains(nodeIndex)) { - MinersDreamTorchPlacer.tryPlaceTorch(level, node.center(), random); - } - - currentNodePositions = null; - positionCursor = 0; - nodeIndex++; - } - - private void spawnBreakParticles(ServerLevel level, BlockPos pos, BlockState state, RandomSource random) { - level.sendParticles(new BlockParticleOption(ParticleTypes.BLOCK, state), - pos.getX() + 0.5D, pos.getY() + 0.5D, pos.getZ() + 0.5D, 2, 0.25D, 0.25D, 0.25D, 0.02D); - if (random.nextFloat() < 0.2F) { - level.sendParticles(new DustParticleOptions(FLYTRAP_GOO_COLOR, 1.0F), - pos.getX() + 0.5D, pos.getY() + 0.5D, pos.getZ() + 0.5D, 1, 0.2D, 0.2D, 0.2D, 0.0D); - } - } - - private void spawnFrontDust(ServerLevel level, MinersDreamCaveGenerator.CaveNode node, RandomSource random) { - BlockPos center = node.center(); - level.sendParticles(ParticleTypes.CLOUD, center.getX() + 0.5D, center.getY() + 0.5D, center.getZ() + 0.5D, - 3, node.horizontalRadius() * 0.4D, node.verticalRadius() * 0.3D, node.horizontalRadius() * 0.4D, 0.01D); - } - - private void playRumble(ServerLevel level, MinersDreamCaveGenerator.CaveNode node) { - BlockPos center = node.center(); - level.playSound(null, center, SoundEvents.STONE_BREAK, SoundSource.BLOCKS, 0.5F, 0.7F); - } - - private void playCompletionSound(ServerLevel level) { - if (nodes.isEmpty()) { - return; - } - BlockPos center = nodes.get(nodes.size() - 1).center(); - level.playSound(null, center, SoundEvents.BEACON_ACTIVATE, SoundSource.PLAYERS, 0.4F, 1.4F); - } -} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/item/MinersDreamExcavationManager.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/item/MinersDreamExcavationManager.java deleted file mode 100644 index 4effa987..00000000 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/item/MinersDreamExcavationManager.java +++ /dev/null @@ -1,81 +0,0 @@ -package com.craisinlord.antarchy.content.item; - -import com.craisinlord.antarchy.config.AntarchySettings; -import java.util.Iterator; -import java.util.Map; -import java.util.UUID; -import java.util.concurrent.ConcurrentHashMap; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.util.Mth; -import net.minecraft.util.RandomSource; - -public final class MinersDreamExcavationManager { - private static final int MAX_ACTIVE_TASKS = 8; - private static final int SAFE_MIN_RANGE = 8; - private static final int SAFE_MAX_RANGE = 256; - private static final Map ACTIVE = new ConcurrentHashMap<>(); - - private MinersDreamExcavationManager() { - } - - public static boolean hasActiveExcavation(UUID owner) { - return ACTIVE.containsKey(owner); - } - - public static MinersDreamExcavation tryStart(ServerLevel level, UUID owner, BlockPos startPos, Direction direction) { - if (ACTIVE.containsKey(owner) || ACTIVE.size() >= MAX_ACTIVE_TASKS) { - return null; - } - - int minRange = Mth.clamp(AntarchySettings.minersDreamMinimumRange(), SAFE_MIN_RANGE, SAFE_MAX_RANGE); - int maxRange = Mth.clamp(AntarchySettings.minersDreamMaximumRange(), SAFE_MIN_RANGE, SAFE_MAX_RANGE); - if (minRange > maxRange) { - int swap = minRange; - minRange = maxRange; - maxRange = swap; - } - - RandomSource random = level.getRandom(); - int length = minRange == maxRange ? minRange : minRange + random.nextInt(maxRange - minRange + 1); - - MinersDreamCaveGenerator.CaveGenerationResult result = MinersDreamCaveGenerator.generate( - startPos, - direction, - length, - AntarchySettings.minersDreamTorchSpacing(), - level.getMinBuildHeight(), - level.getMaxBuildHeight(), - random - ); - - if (result.nodes().isEmpty()) { - return null; - } - - MinersDreamExcavation excavation = new MinersDreamExcavation(owner, level.dimension(), result.nodes(), result.torchCheckpoints()); - ACTIVE.put(owner, excavation); - return excavation; - } - - public static void tick(ServerLevel level) { - if (ACTIVE.isEmpty()) { - return; - } - - int budget = Math.max(1, AntarchySettings.minersDreamBlocksPerTick()); - Iterator> iterator = ACTIVE.entrySet().iterator(); - while (iterator.hasNext()) { - MinersDreamExcavation excavation = iterator.next().getValue(); - if (!excavation.dimension().equals(level.dimension())) { - continue; - } - - excavation.tick(level, budget); - if (excavation.isFinished()) { - iterator.remove(); - } - } - } -} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/item/MinersDreamItem.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/item/MinersDreamItem.java deleted file mode 100644 index d0ec451f..00000000 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/item/MinersDreamItem.java +++ /dev/null @@ -1,79 +0,0 @@ -package com.craisinlord.antarchy.content.item; - -import com.craisinlord.antarchy.config.AntarchySettings; -import java.util.List; -import net.minecraft.ChatFormatting; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.network.chat.Component; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.sounds.SoundEvents; -import net.minecraft.sounds.SoundSource; -import net.minecraft.world.InteractionResult; -import net.minecraft.world.entity.player.Player; -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; -import net.minecraft.world.level.Level; - -public final class MinersDreamItem extends Item { - public MinersDreamItem(Properties properties) { - super(properties); - } - - @Override - public InteractionResult useOn(UseOnContext context) { - Level level = context.getLevel(); - Player player = context.getPlayer(); - if (player == null) { - return InteractionResult.PASS; - } - - if (!AntarchySettings.minersDreamEnabled()) { - if (!level.isClientSide()) { - player.displayClientMessage(Component.translatable("message.antarchy.miners_dream_disabled"), true); - } - return InteractionResult.FAIL; - } - - if (level.isClientSide()) { - return InteractionResult.SUCCESS; - } - - ServerLevel serverLevel = (ServerLevel) level; - BlockPos clickedPos = context.getClickedPos(); - Direction clickedFace = context.getClickedFace(); - Direction travelDirection = clickedFace.getAxis().isHorizontal() ? clickedFace : player.getDirection(); - BlockPos startPos = clickedPos.relative(travelDirection); - - if (!serverLevel.isInWorldBounds(startPos) || !MinersDreamCaveGenerator.isChunkLoaded(serverLevel, startPos)) { - return InteractionResult.FAIL; - } - - if (MinersDreamExcavationManager.hasActiveExcavation(player.getUUID())) { - player.displayClientMessage(Component.translatable("message.antarchy.miners_dream_busy"), true); - return InteractionResult.FAIL; - } - - MinersDreamExcavation excavation = MinersDreamExcavationManager.tryStart(serverLevel, player.getUUID(), startPos, travelDirection); - if (excavation == null) { - return InteractionResult.FAIL; - } - - serverLevel.playSound(null, clickedPos, SoundEvents.TNT_PRIMED, SoundSource.PLAYERS, 0.8F, 1.1F); - - if (!player.getAbilities().instabuild) { - context.getItemInHand().shrink(1); - } - - return InteractionResult.CONSUME; - } - - @Override - public void appendHoverText(ItemStack stack, Item.TooltipContext context, List tooltipComponents, TooltipFlag tooltipFlag) { - tooltipComponents.add(Component.translatable("tooltip.antarchy.miners_dream.line1").withStyle(ChatFormatting.GRAY)); - tooltipComponents.add(Component.translatable("tooltip.antarchy.miners_dream.line2").withStyle(ChatFormatting.GRAY)); - super.appendHoverText(stack, context, tooltipComponents, tooltipFlag); - } -} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/item/MinersDreamTorchPlacer.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/item/MinersDreamTorchPlacer.java deleted file mode 100644 index 08e8f78b..00000000 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/item/MinersDreamTorchPlacer.java +++ /dev/null @@ -1,89 +0,0 @@ -package com.craisinlord.antarchy.content.item; - -import java.util.List; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.sounds.SoundSource; -import net.minecraft.util.RandomSource; -import net.minecraft.world.level.block.Blocks; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.block.state.properties.BlockStateProperties; - -public final class MinersDreamTorchPlacer { - private MinersDreamTorchPlacer() { - } - - public static void tryPlaceTorch(ServerLevel level, BlockPos center, RandomSource random) { - BlockPos floorSpot = findFloorSpot(level, center); - boolean floorFirst = random.nextBoolean(); - - if (floorFirst) { - if (floorSpot != null && placeFloorTorch(level, floorSpot)) { - return; - } - placeWallTorch(level, center, random); - return; - } - - if (placeWallTorch(level, center, random)) { - return; - } - if (floorSpot != null) { - placeFloorTorch(level, floorSpot); - } - } - - private static BlockPos findFloorSpot(ServerLevel level, BlockPos center) { - for (int dy = -2; dy <= 1; dy++) { - BlockPos candidate = center.offset(0, dy, 0); - if (!isOpenForTorch(level, candidate)) { - continue; - } - BlockState below = level.getBlockState(candidate.below()); - if (below.isFaceSturdy(level, candidate.below(), Direction.UP)) { - return candidate; - } - } - return null; - } - - private static boolean placeFloorTorch(ServerLevel level, BlockPos pos) { - if (!isOpenForTorch(level, pos)) { - return false; - } - BlockState state = Blocks.TORCH.defaultBlockState(); - if (!state.canSurvive(level, pos)) { - return false; - } - level.setBlock(pos, state, 3); - level.playSound(null, pos, state.getSoundType().getPlaceSound(), SoundSource.BLOCKS, 1.0F, 1.0F); - return true; - } - - private static boolean placeWallTorch(ServerLevel level, BlockPos center, RandomSource random) { - List order = Direction.Plane.HORIZONTAL.shuffledCopy(random); - for (int dy = -1; dy <= 1; dy++) { - BlockPos candidate = center.offset(0, dy, 0); - if (!isOpenForTorch(level, candidate)) { - continue; - } - for (Direction wallSide : order) { - Direction facing = wallSide.getOpposite(); - BlockState state = Blocks.WALL_TORCH.defaultBlockState().setValue(BlockStateProperties.HORIZONTAL_FACING, facing); - if (!state.canSurvive(level, candidate)) { - continue; - } - level.setBlock(candidate, state, 3); - level.playSound(null, candidate, state.getSoundType().getPlaceSound(), SoundSource.BLOCKS, 1.0F, 1.0F); - return true; - } - } - return false; - } - - private static boolean isOpenForTorch(ServerLevel level, BlockPos pos) { - BlockState state = level.getBlockState(pos); - return (state.isAir() || state.canBeReplaced()) && level.getFluidState(pos).isEmpty(); - } -} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/item/PortalGunItem.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/item/PortalGunItem.java index 53fa0932..3ff744e2 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/item/PortalGunItem.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/item/PortalGunItem.java @@ -3,6 +3,8 @@ package com.craisinlord.antarchy.content.item; import com.craisinlord.antarchy.Antarchy; import com.craisinlord.antarchy.content.block.entity.PortalGunPortalBaseBlockEntity; import com.craisinlord.antarchy.content.block.entity.PortalGunPortalMasterBlockEntity; +import com.craisinlord.antarchy.content.block.entity.PortalGunPortalFaceRecord; +import com.craisinlord.antarchy.content.block.entity.PortalGunPortalCellAccess; import com.craisinlord.antarchy.content.client.model.ResourceBackedGeoItemModel; import com.craisinlord.antarchy.content.client.renderer.AnimatedHeldItemRenderer; import com.craisinlord.antarchy.content.network.PortalGunPrimaryPayload; @@ -11,11 +13,14 @@ import com.craisinlord.antarchy.content.portalgun.PortalGunPlacement; import com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity; import com.craisinlord.antarchy.content.portalgun.PortalGunProjectileEntity; import com.craisinlord.antarchy.content.portalgun.PortalGunSavedData; +import com.craisinlord.antarchy.content.portalgun.PortalGunResetManager; +import com.craisinlord.antarchy.config.AntarchySettings; import java.util.HashSet; import java.util.List; import java.util.Optional; import java.util.Set; import java.util.UUID; +import java.nio.charset.StandardCharsets; import java.util.function.Consumer; import java.util.function.Supplier; import net.minecraft.core.BlockPos; @@ -27,8 +32,6 @@ import net.minecraft.network.chat.Component; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; -import net.minecraft.sounds.SoundEvent; -import net.minecraft.sounds.SoundEvents; import net.minecraft.sounds.SoundSource; import net.minecraft.stats.Stats; import net.minecraft.world.InteractionHand; @@ -52,6 +55,7 @@ import net.minecraft.world.level.material.FluidState; import net.minecraft.world.phys.BlockHitResult; import net.minecraft.world.phys.HitResult; import net.minecraft.world.phys.Vec3; +import net.minecraft.util.Mth; import software.bernie.geckolib.animatable.GeoItem; import software.bernie.geckolib.animatable.client.GeoRenderProvider; import software.bernie.geckolib.animatable.instance.AnimatableInstanceCache; @@ -69,6 +73,15 @@ public class PortalGunItem extends Item implements GeoItem { private static final String LAST_SIDE_TAG = "antarchy.portal_gun_last_side"; private static final String MOON_SIDE_TAG = "antarchy.portal_gun_moon_side"; public static final String GUN_ID_TAG = "antarchy.portal_gun_id"; + public static final String CHANNEL_NAME_TAG = "antarchy.portal_gun_channel"; + public static final String OWNER_ID_TAG = "antarchy.portal_gun_owner"; + public static final String OWNER_NAME_TAG = "antarchy.portal_gun_owner_name"; + public static final UUID GLOBAL_OWNER_ID = UUID.nameUUIDFromBytes("Global".getBytes(StandardCharsets.UTF_8)); + public static final String PORTAL_WIDTH_TAG = "width"; + public static final String PORTAL_HEIGHT_TAG = "height"; + public static final String GRAB_STRENGTH_TAG = "grabStrength"; + private static final int MAX_PORTAL_WIDTH = 16; + private static final int MAX_PORTAL_HEIGHT = 16; private static final ResourceLocation MODEL_LOCATION = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "geo/portal_gun.geo.json"); private static final ResourceLocation TEXTURE_LOCATION = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "textures/models/item/portal_gun.png"); private static final ResourceLocation ANIMATION_LOCATION = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "animations/portal_gun.animation.json"); @@ -104,81 +117,126 @@ public class PortalGunItem extends Item implements GeoItem { } ServerLevel serverLevel = (ServerLevel) level; if (player.isShiftKeyDown()) { - PortalGunSavedData.clearAllPortals(serverLevel.getServer(), player.getUUID()); - this.setLastSide(stack, null); - this.setMoonSide(stack, null); - level.playSound(null, player.getX(), player.getY(), player.getZ(), PortalGunPortalEntity.sound("portal_fizzle", SoundEvents.ENDER_CHEST_CLOSE), SoundSource.PLAYERS, 0.6F, 1.0F); + if (player instanceof ServerPlayer serverPlayer) { + this.resetPortals(serverPlayer, stack); + } return InteractionResultHolder.consume(stack); } - boolean fired = this.firePortal(serverLevel, player, stack, PortalGunPortalEntity.PortalSide.ORANGE); + if (player instanceof ServerPlayer serverPlayer && PortalGunResetManager.handleOrangeReset(serverPlayer, stack)) { + return InteractionResultHolder.consume(stack); + } + boolean fired = this.firePortal(serverLevel, player, stack, PortalGunPortalEntity.PortalSide.ORANGE, usedHand == InteractionHand.OFF_HAND); return fired ? InteractionResultHolder.consume(stack) : InteractionResultHolder.fail(stack); } - public void firePrimary(ServerLevel level, ServerPlayer player, ItemStack stack) { - this.firePortal(level, player, stack, PortalGunPortalEntity.PortalSide.BLUE); + public void firePrimary(ServerLevel level, ServerPlayer player, ItemStack stack, boolean offhand) { + this.firePortal(level, player, stack, PortalGunPortalEntity.PortalSide.BLUE, offhand); } - private boolean firePortal(ServerLevel level, Player player, ItemStack stack, PortalGunPortalEntity.PortalSide side) { + public void resetPortals(ServerPlayer player, ItemStack stack) { + UUID gunId = this.ensureGunId(stack, player.getUUID(), player.getGameProfile().getName()); + UUID ownerId = getPortalOwnerId(stack, player.getUUID()); + PortalGunSavedData.clearAllPortals(player.serverLevel(), ownerId, gunId, getChannelName(stack)); + this.setLastSide(stack, null); + this.setMoonSide(stack, null); + player.serverLevel().playSound(null, player.getX(), player.getY(), player.getZ(), PortalGunPortalEntity.sound("portal_fizzle"), SoundSource.PLAYERS, 0.6F, 1.0F); + } + + public void resetPortalSide(ServerPlayer player, ItemStack stack, PortalGunPortalEntity.PortalSide side) { + UUID gunId = this.ensureGunId(stack, player.getUUID(), player.getGameProfile().getName()); + UUID ownerId = getPortalOwnerId(stack, player.getUUID()); + PortalGunSavedData.clearPortalSide(player.serverLevel(), ownerId, gunId, getChannelName(stack), side); + player.serverLevel().playSound(null, player.getX(), player.getY(), player.getZ(), PortalGunPortalEntity.sound("portal_fizzle"), SoundSource.PLAYERS, 0.6F, 1.0F); + } + + public void fireMobPortal(ServerLevel level, LivingEntity shooter, ItemStack stack, PortalGunPortalEntity.PortalSide side) { + this.ensureGunId(stack, shooter.getUUID(), shooter.getName().getString()); + String firePath = side == PortalGunPortalEntity.PortalSide.BLUE ? "portal_gun_fire_blue" : "portal_gun_fire_orange"; + level.playSound(null, shooter.getX(), shooter.getY(), shooter.getZ(), PortalGunPortalEntity.sound(firePath), shooter.getSoundSource(), 0.65F, side == PortalGunPortalEntity.PortalSide.BLUE ? 1.15F : 0.88F); + this.spawnProjectile(level, shooter, stack, side); + } + + private boolean firePortal(ServerLevel level, Player player, ItemStack stack, PortalGunPortalEntity.PortalSide side, boolean offhand) { if (this.raycast(level, player).getType() == HitResult.Type.MISS) { - if (this.tryArmMoonShot(level, player, stack, side)) { + if (this.tryArmMoonShot(level, player, stack, side, offhand)) { return true; } - level.playSound(null, player.getX(), player.getY(), player.getZ(), PortalGunPortalEntity.sound("portal_gun_invalid_surface", SoundEvents.DISPENSER_FAIL), SoundSource.PLAYERS, 0.55F, 1.0F); - return false; } this.triggerFireAnimation(level, player, stack); - this.setLastSide(stack, side); - stack.hurtAndBreak(1, player, EquipmentSlot.MAINHAND); + this.ensureGunId(stack, player.getUUID(), player.getGameProfile().getName()); + stack.hurtAndBreak(1, player, offhand ? EquipmentSlot.OFFHAND : EquipmentSlot.MAINHAND); player.awardStat(Stats.ITEM_USED.get(this)); player.getCooldowns().addCooldown(this, 6); String firePath = side == PortalGunPortalEntity.PortalSide.BLUE ? "portal_gun_fire_blue" : "portal_gun_fire_orange"; - level.playSound(null, player.getX(), player.getY(), player.getZ(), PortalGunPortalEntity.sound(firePath, SoundEvents.BEACON_ACTIVATE), SoundSource.PLAYERS, 0.65F, side == PortalGunPortalEntity.PortalSide.BLUE ? 1.15F : 0.88F); + level.playSound(null, player.getX(), player.getY(), player.getZ(), PortalGunPortalEntity.sound(firePath), SoundSource.PLAYERS, 0.65F, side == PortalGunPortalEntity.PortalSide.BLUE ? 1.15F : 0.88F); this.spawnProjectile(level, player, stack, side); return true; } - public void handlePortalImpact(ServerLevel level, Player player, ItemStack stack, PortalGunPortalEntity.PortalSide side, BlockHitResult hitResult, Vec3 impactPos) { - PortalGunPortalEntity existing = this.findPlacedPortal(level, player.getUUID(), side); - PortalGunPlacement placement = this.findPlacement(level, player, side, hitResult, existing); + public void handlePortalImpact(ServerLevel level, LivingEntity shooter, ItemStack stack, UUID gunId, PortalGunPortalEntity.PortalSide side, BlockHitResult hitResult, Vec3 impactPos, int portalWidth, int portalHeight) { + UUID portalOwnerId = getPortalOwnerId(stack, shooter.getUUID()); + String portalOwnerIdentity = getPortalOwnerIdentity(stack, shooter.getUUID()); + String channelName = getChannelName(stack); + PortalGunPortalEntity existing = this.findPlacedPortal(level, portalOwnerId, gunId, channelName, side); + PortalGunPlacement placement = this.findPlacement(level, shooter, portalOwnerId, channelName, side, hitResult, existing, portalWidth, portalHeight); if (placement == null) { - Antarchy.LOGGER.info("Portal gun placement failed owner={} side={} hitBlock={} hitFace={} hitLocation={}", player.getUUID(), side, hitResult.getBlockPos(), hitResult.getDirection(), hitResult.getLocation()); - level.playSound(null, impactPos.x, impactPos.y, impactPos.z, PortalGunPortalEntity.sound("portal_gun_invalid_surface", SoundEvents.DISPENSER_FAIL), SoundSource.PLAYERS, 0.55F, 1.0F); + Antarchy.LOGGER.debug("Portal gun placement failed owner={} side={} hitBlock={} hitFace={} hitLocation={}", shooter.getUUID(), side, hitResult.getBlockPos(), hitResult.getDirection(), hitResult.getLocation()); + level.playSound(null, impactPos.x, impactPos.y, impactPos.z, PortalGunPortalEntity.sound("portal_gun_invalid_surface"), SoundSource.PLAYERS, 0.55F, 1.0F); + return; + } + if (existing != null && this.isSamePortalPlacement(existing, placement)) { + existing.restartOpeningAnimation(); + if (stack.getItem() == this) { + this.setLastSide(stack, side); + } + level.playSound(null, existing.getX(), existing.getY(), existing.getZ(), PortalGunPortalEntity.sound("portal_fizzle"), SoundSource.PLAYERS, 0.6F, 1.0F); return; } if (existing != null) { - Antarchy.LOGGER.info("Portal gun replacing existing portal owner={} side={} oldPortal={}", player.getUUID(), side, existing.getUUID()); + Antarchy.LOGGER.debug("Portal gun replacing existing portal owner={} side={} oldPortal={}", shooter.getUUID(), side, existing.getUUID()); existing.discard(); } if (this.isMoonSideArmed(stack, side)) { this.setMoonSide(stack, null); } - if (this.isMoonSideArmed(stack, this.otherSide(side))) { + if (shooter instanceof Player player && this.isMoonSideArmed(stack, this.otherSide(side))) { this.spawnBlackHole(level, player, stack, side, placement.center()); return; } PortalGunPortalEntity portal = new PortalGunPortalEntity(this.portalType.get(), level); - portal.configure(player.getUUID(), side, placement); + portal.configure(portalOwnerId, gunId, portalOwnerIdentity, channelName, side, placement); portal.moveTo(placement.center().x, placement.center().y, placement.center().z, placement.yaw(), 0.0F); - if (!this.placePortalBlocks(level, player.getUUID(), side, portal, placement)) { - Antarchy.LOGGER.info("Portal gun block placement failed owner={} side={} portal={} center={} facing={} up={} master={} base={}", player.getUUID(), side, portal.getUUID(), placement.center(), placement.facing(), placement.upAxis(), placement.masterPos(), placement.basePos()); - level.playSound(null, impactPos.x, impactPos.y, impactPos.z, PortalGunPortalEntity.sound("portal_gun_invalid_surface", SoundEvents.DISPENSER_FAIL), SoundSource.PLAYERS, 0.55F, 1.0F); + if (!this.placePortalBlocks(level, portalOwnerId, gunId, portalOwnerIdentity, channelName, side, portal, placement)) { + Antarchy.LOGGER.debug("Portal gun block placement failed owner={} side={} portal={} center={} facing={} up={} master={} base={}", shooter.getUUID(), side, portal.getUUID(), placement.center(), placement.facing(), placement.upAxis(), placement.masterPos(), placement.basePos()); + level.playSound(null, impactPos.x, impactPos.y, impactPos.z, PortalGunPortalEntity.sound("portal_gun_invalid_surface"), SoundSource.PLAYERS, 0.55F, 1.0F); return; } level.addFreshEntity(portal); - PortalGunSavedData.setPortal(level.getServer(), player.getUUID(), side, portal.getUUID()); - Antarchy.LOGGER.info("Portal gun portal placed owner={} side={} portal={} center={} facing={} up={} width={} master={} base={}", player.getUUID(), side, portal.getUUID(), placement.center(), placement.facing(), placement.upAxis(), placement.widthAxis(), placement.masterPos(), placement.basePos()); - PortalGunPortalEntity other = this.findCounterpart(level, player.getUUID(), side); + PortalGunSavedData.setPortal(level, portalOwnerId, gunId, channelName, side, portal.getUUID()); + PortalGunPortalEntity other = this.findCounterpart(level, portalOwnerId, gunId, channelName, side); if (other != null) { portal.linkTo(other); other.linkTo(portal); this.syncPortalBlocks(level, portal); this.syncPortalBlocks(level, other); - Antarchy.LOGGER.info("Portal gun portals linked owner={} placedSide={} placedPortal={} otherPortal={} otherSide={}", player.getUUID(), side, portal.getUUID(), other.getUUID(), other.getPortalSide()); - } else { - Antarchy.LOGGER.info("Portal gun portal waiting for counterpart owner={} side={} portal={}", player.getUUID(), side, portal.getUUID()); + } + if (stack.getItem() == this) { + this.setLastSide(stack, side); } String openPath = side == PortalGunPortalEntity.PortalSide.BLUE ? "portal_open_blue" : "portal_open_orange"; - level.playSound(null, portal.getX(), portal.getY(), portal.getZ(), PortalGunPortalEntity.sound(openPath, SoundEvents.END_PORTAL_SPAWN), SoundSource.PLAYERS, 0.45F, side == PortalGunPortalEntity.PortalSide.BLUE ? 1.05F : 0.92F); + level.playSound(null, portal.getX(), portal.getY(), portal.getZ(), PortalGunPortalEntity.sound(openPath), SoundSource.PLAYERS, 0.45F, side == PortalGunPortalEntity.PortalSide.BLUE ? 1.05F : 0.92F); + } + + private boolean isSamePortalPlacement(PortalGunPortalEntity portal, PortalGunPlacement placement) { + if (portal.getPortalWidth() != placement.width() + || portal.getPortalHeight() != placement.height() + || portal.getFacingDirection() != placement.facing() + || portal.getUpAxis() != placement.upAxis() + || !portal.getMasterPos().equals(placement.masterPos())) { + return false; + } + return new HashSet<>(java.util.Arrays.asList(portal.getPortalSpots())) + .equals(new HashSet<>(java.util.Arrays.asList(placement.portalSpots()))); } public ItemStack findMatchingGunStack(Player player, UUID gunId) { @@ -199,21 +257,22 @@ public class PortalGunItem extends Item implements GeoItem { return ItemStack.EMPTY; } - private boolean tryArmMoonShot(ServerLevel level, Player player, ItemStack stack, PortalGunPortalEntity.PortalSide side) { + private boolean tryArmMoonShot(ServerLevel level, Player player, ItemStack stack, PortalGunPortalEntity.PortalSide side, boolean offhand) { if (!level.dimensionType().hasSkyLight() || !level.isNight() || player.getXRot() > -40.0F) { return false; } - PortalGunPortalEntity existing = this.findPlacedPortal(level, player.getUUID(), side); + UUID ownerId = getPortalOwnerId(stack, player.getUUID()); + PortalGunPortalEntity existing = this.findPlacedPortal(level, ownerId, this.ensureGunId(stack, player.getUUID(), player.getGameProfile().getName()), getChannelName(stack), side); if (existing != null) { existing.discard(); } this.setMoonSide(stack, side); this.setLastSide(stack, side); this.triggerFireAnimation(level, player, stack); - stack.hurtAndBreak(1, player, EquipmentSlot.MAINHAND); + stack.hurtAndBreak(1, player, offhand ? EquipmentSlot.OFFHAND : EquipmentSlot.MAINHAND); player.awardStat(Stats.ITEM_USED.get(this)); player.getCooldowns().addCooldown(this, 6); - level.playSound(null, player.getX(), player.getY(), player.getZ(), PortalGunPortalEntity.sound(side == PortalGunPortalEntity.PortalSide.BLUE ? "portal_gun_fire_blue" : "portal_gun_fire_orange", SoundEvents.BEACON_ACTIVATE), SoundSource.PLAYERS, 0.65F, side == PortalGunPortalEntity.PortalSide.BLUE ? 1.15F : 0.88F); + level.playSound(null, player.getX(), player.getY(), player.getZ(), PortalGunPortalEntity.sound(side == PortalGunPortalEntity.PortalSide.BLUE ? "portal_gun_fire_blue" : "portal_gun_fire_orange"), SoundSource.PLAYERS, 0.65F, side == PortalGunPortalEntity.PortalSide.BLUE ? 1.15F : 0.88F); this.spawnShotTrail(level, player, player.getEyePosition().add(player.getLookAngle().scale(64.0D)), side); return true; } @@ -224,7 +283,7 @@ public class PortalGunItem extends Item implements GeoItem { blackHole.configure(player.getUUID()); blackHole.moveTo(spawnPos.x, spawnPos.y, spawnPos.z, 0.0F, 0.0F); level.addFreshEntity(blackHole); - level.playSound(null, spawnPos.x, spawnPos.y, spawnPos.z, PortalGunPortalEntity.sound("portal_gun_black_hole", SoundEvents.WARDEN_SONIC_BOOM), SoundSource.PLAYERS, 0.6F, 0.6F); + level.playSound(null, spawnPos.x, spawnPos.y, spawnPos.z, PortalGunPortalEntity.sound("portal_open_orange"), SoundSource.PLAYERS, 0.6F, 0.6F); return true; } @@ -241,20 +300,38 @@ public class PortalGunItem extends Item implements GeoItem { } } - private void spawnProjectile(ServerLevel level, Player player, ItemStack stack, PortalGunPortalEntity.PortalSide side) { - UUID gunId = this.ensureGunId(stack); - PortalGunProjectileEntity projectile = new PortalGunProjectileEntity(this.projectileType.get(), player, level); + private void spawnProjectile(ServerLevel level, LivingEntity shooter, ItemStack stack, PortalGunPortalEntity.PortalSide side) { + UUID gunId = this.ensureGunId(stack, shooter.getUUID(), shooter.getName().getString()); + PortalGunProjectileEntity projectile = new PortalGunProjectileEntity(this.projectileType.get(), shooter, level); projectile.configure(side, gunId, stack); - Vec3 look = player.getLookAngle(); - Vec3 start = player.getEyePosition().add(look.scale(0.4D)); + Vec3 look = shooter.getLookAngle(); + Vec3 start = shooter.getEyePosition(); + double launchSpeed = PortalGunProjectileEntity.MIN_LAUNCH_SPEED + level.getRandom().nextDouble() * PortalGunProjectileEntity.LAUNCH_SPEED_VARIANCE; + projectile.setProjectileSpeed(launchSpeed); projectile.setPos(start.x, start.y, start.z); - projectile.setDeltaMovement(look.scale(2.5D)); + projectile.setDeltaMovement(look.scale(projectile.getProjectileSpeed())); + projectile.syncLaunchState(); level.addFreshEntity(projectile); } - private PortalGunPortalEntity findCounterpart(ServerLevel level, UUID owner, PortalGunPortalEntity.PortalSide side) { + public static int getPortalWidth(ItemStack stack) { + net.minecraft.world.item.component.CustomData data = stack.get(DataComponents.CUSTOM_DATA); + return Mth.clamp(data == null ? 1 : data.copyTag().getInt(PORTAL_WIDTH_TAG), 1, MAX_PORTAL_WIDTH); + } + + public static int getPortalHeight(ItemStack stack) { + net.minecraft.world.item.component.CustomData data = stack.get(DataComponents.CUSTOM_DATA); + return Mth.clamp(data == null || !data.copyTag().contains(PORTAL_HEIGHT_TAG) ? 2 : data.copyTag().getInt(PORTAL_HEIGHT_TAG), 2, MAX_PORTAL_HEIGHT); + } + + public static int getGrabStrength(ItemStack stack) { + net.minecraft.world.item.component.CustomData data = stack.get(DataComponents.CUSTOM_DATA); + return Mth.clamp(data == null || !data.copyTag().contains(GRAB_STRENGTH_TAG) ? 4 : data.copyTag().getInt(GRAB_STRENGTH_TAG), 1, 4); + } + + private PortalGunPortalEntity findCounterpart(ServerLevel level, UUID owner, UUID gunId, String channelName, PortalGunPortalEntity.PortalSide side) { PortalGunPortalEntity.PortalSide otherSide = side == PortalGunPortalEntity.PortalSide.BLUE ? PortalGunPortalEntity.PortalSide.ORANGE : PortalGunPortalEntity.PortalSide.BLUE; - Optional otherId = PortalGunSavedData.getPortalId(level.getServer(), owner, otherSide); + Optional otherId = PortalGunSavedData.getPortalId(level.getServer(), owner, gunId, channelName, otherSide, level.dimension().location()); if (otherId.isEmpty()) { return null; } @@ -262,8 +339,8 @@ public class PortalGunItem extends Item implements GeoItem { return entity instanceof PortalGunPortalEntity portal ? portal : null; } - private PortalGunPortalEntity findPlacedPortal(ServerLevel level, UUID owner, PortalGunPortalEntity.PortalSide side) { - Optional portalId = PortalGunSavedData.getPortalId(level.getServer(), owner, side); + private PortalGunPortalEntity findPlacedPortal(ServerLevel level, UUID owner, UUID gunId, String channelName, PortalGunPortalEntity.PortalSide side) { + Optional portalId = PortalGunSavedData.getPortalId(level.getServer(), owner, gunId, channelName, side, level.dimension().location()); if (portalId.isEmpty()) { return null; } @@ -281,134 +358,245 @@ public class PortalGunItem extends Item implements GeoItem { return level.clip(new ClipContext(start, end, ClipContext.Block.COLLIDER, ClipContext.Fluid.NONE, player)); } - private PortalGunPlacement findPlacement(Level level, Player player, PortalGunPortalEntity.PortalSide side, BlockHitResult hitResult, PortalGunPortalEntity replacingPortal) { + private PortalGunPlacement findPlacement(Level level, LivingEntity shooter, UUID ownerId, String channelName, PortalGunPortalEntity.PortalSide side, BlockHitResult hitResult, PortalGunPortalEntity replacingPortal, int requestedWidth, int requestedHeight) { + int maxWidth = Mth.clamp(requestedWidth, 1, 16); + int maxHeight = Mth.clamp(requestedHeight, 2, 16); + boolean canResize = AntarchySettings.portalGunCanPortalsResizeWhenCreated(); + for (int height = maxHeight; height >= 2; height--) { + for (int width = maxWidth; width >= 1; width--) { + if (!canResize && (width != maxWidth || height != maxHeight)) { + continue; + } + PortalGunPlacement placement = this.findPlacementForSize(level, shooter, ownerId, channelName, side, hitResult, replacingPortal, width, height); + if (placement != null) { + return placement; + } + } + } + return null; + } + + private PortalGunPlacement findPlacementForSize(Level level, LivingEntity shooter, UUID ownerId, String channelName, PortalGunPortalEntity.PortalSide side, BlockHitResult hitResult, PortalGunPortalEntity replacingPortal, int width, int height) { Direction facing = hitResult.getDirection(); - Direction heightAxis = this.resolveUpAxis(player, facing); + Direction heightAxis = this.resolveUpAxis(shooter, facing); Direction widthAxis = PortalGunPlacement.widthAxis(facing, heightAxis); float yaw = PortalGunPlacement.yawFor(facing, heightAxis); BlockPos hitPos = hitResult.getBlockPos(); - Vec3 hitLocation = hitResult.getLocation(); - UUID replacingPortalId = replacingPortal == null ? null : replacingPortal.getUUID(); - PortalGunPlacement bestPlacement = null; - double bestScore = Double.MAX_VALUE; - for (int widthOffset = -3; widthOffset <= 3; widthOffset++) { - for (int heightOffset = -4; heightOffset <= 3; heightOffset++) { - BlockPos supportOrigin = hitPos.relative(widthAxis, widthOffset).relative(heightAxis, heightOffset); - if (!this.canPlaceAt(level, player.getUUID(), side, supportOrigin, facing, heightAxis, replacingPortalId)) { - continue; - } - BlockPos masterPos = supportOrigin.relative(facing); - BlockPos basePos = masterPos.relative(heightAxis); - BlockPos[] portalSpots = new BlockPos[] {masterPos, basePos}; - Vec3 firstCellCenter = masterPos.getCenter(); - Vec3 center = firstCellCenter - .add(Vec3.atLowerCornerOf(heightAxis.getNormal()).scale(0.5D)) - .subtract(Vec3.atLowerCornerOf(facing.getNormal()).scale(0.5D)); - Vec3 local = hitLocation.subtract(center); - double widthLocal = local.dot(Vec3.atLowerCornerOf(widthAxis.getNormal())); - double heightLocal = local.dot(Vec3.atLowerCornerOf(heightAxis.getNormal())); - double depthLocal = local.dot(Vec3.atLowerCornerOf(facing.getNormal())); - double widthDistance = Math.max(0.0D, Math.abs(widthLocal) - 0.5D); - double heightDistance = Math.max(0.0D, Math.abs(heightLocal) - 1.0D); - double score = widthDistance * widthDistance - + heightDistance * heightDistance - + Math.abs(depthLocal) * 0.25D - + supportOrigin.distSqr(hitPos) * 0.12D - + Math.abs(widthOffset) * 0.04D - + Math.abs(heightOffset) * 0.025D - + (widthOffset != 0 || heightOffset != 0 ? 0.02D : 0.0D); - if (score < bestScore) { - bestScore = score; - Set compensatedSpots = new HashSet<>(); - if (widthOffset != 0 || heightOffset != 0) { - compensatedSpots.add(hitPos.relative(facing)); - compensatedSpots.add(masterPos); - compensatedSpots.add(basePos); - compensatedSpots.add(supportOrigin); - compensatedSpots.add(basePos.relative(facing.getOpposite())); + int maxRadius = Math.max(width, height); + List radiusOrder = new java.util.ArrayList<>(maxRadius); + for (int radius = 0; radius < maxRadius; radius++) { + radiusOrder.add(radius); + } + int preferredRadius = maxRadius / 2; + radiusOrder.remove(Integer.valueOf(preferredRadius)); + radiusOrder.add(0, preferredRadius); + for (int radius : radiusOrder) { + for (int horizontalOffset = radius; horizontalOffset >= 0; horizontalOffset--) { + for (int verticalOffset = radius; verticalOffset >= 0; verticalOffset--) { + List supports = new java.util.ArrayList<>(width * height); + for (int widthCell = 1 - width; widthCell <= 0; widthCell++) { + for (int heightCell = 1 - height; heightCell <= 0; heightCell++) { + supports.add(this.referenceSupportPosition(hitPos, facing, heightAxis, horizontalOffset, verticalOffset, widthCell, heightCell)); + } } - bestPlacement = new PortalGunPlacement(center, facing, heightAxis, widthAxis, yaw, supportOrigin, masterPos, basePos, portalSpots, compensatedSpots); + if (!supports.contains(hitPos)) { + continue; + } + BlockPos[] portalSpots = new BlockPos[supports.size()]; + boolean valid = true; + int spotIndex = 0; + for (BlockPos supportPos : supports) { + BlockPos portalPos = supportPos.relative(facing); + BlockState supportState = level.getBlockState(supportPos); + BlockState portalState = level.getBlockState(portalPos); + if (!supportState.isFaceSturdy(level, supportPos, facing) + || !this.isPortalSpaceAvailable(level, portalPos, portalState, ownerId, channelName, side, facing)) { + valid = false; + break; + } + portalSpots[spotIndex++] = portalPos; + } + if (!valid) { + continue; + } + BlockPos masterPos = null; + for (BlockPos portalSpot : portalSpots) { + BlockState state = level.getBlockState(portalSpot); + boolean belongsToReplacingPortal = replacingPortal != null && replacingPortal.containsPortalSpot(portalSpot); + boolean existingPortalCell = level.getBlockEntity(portalSpot) instanceof PortalGunPortalCellAccess; + if ((state.canBeReplaced() || belongsToReplacingPortal || existingPortalCell) && level.getFluidState(portalSpot).isEmpty()) { + masterPos = portalSpot; + break; + } + } + if (masterPos == null) { + return null; + } + BlockPos[] orderedPortalSpots = new BlockPos[portalSpots.length]; + orderedPortalSpots[0] = masterPos; + int orderedSpotIndex = 1; + for (BlockPos portalSpot : portalSpots) { + if (!portalSpot.equals(masterPos)) { + orderedPortalSpots[orderedSpotIndex++] = portalSpot; + } + } + portalSpots = orderedPortalSpots; + Vec3 center = Vec3.ZERO; + Set compensatedSpots = new HashSet<>(); + for (BlockPos portalSpot : portalSpots) { + center = center.add(portalSpot.getCenter()); + if (horizontalOffset != 0 || verticalOffset != 0) { + compensatedSpots.add(portalSpot); + compensatedSpots.add(portalSpot.relative(facing.getOpposite())); + } + } + center = center.scale(1.0D / portalSpots.length) + .subtract(Vec3.atLowerCornerOf(facing.getNormal()).scale(0.5D)); + BlockPos supportOrigin = masterPos.relative(facing.getOpposite()); + BlockPos basePos = portalSpots.length > 1 ? portalSpots[1] : portalSpots[0]; + return new PortalGunPlacement(center, facing, heightAxis, widthAxis, yaw, supportOrigin, masterPos, basePos, portalSpots, compensatedSpots, width, height); } } } - return bestPlacement; + return null; } - private Direction resolveUpAxis(Player player, Direction facing) { + private BlockPos referenceSupportPosition(BlockPos origin, Direction facing, Direction upAxis, int horizontalOffset, int verticalOffset, int widthCell, int heightCell) { + int x; + int y; + int z; + if (facing.getAxis() != Direction.Axis.Y) { + if (facing.getAxis() == Direction.Axis.Z) { + x = horizontalOffset + widthCell; + z = 0; + } else { + x = 0; + z = horizontalOffset + widthCell; + } + y = -verticalOffset - heightCell; + } else if (upAxis == Direction.NORTH) { + x = horizontalOffset + widthCell; + y = 0; + z = verticalOffset + heightCell; + } else if (upAxis == Direction.SOUTH) { + x = horizontalOffset + widthCell; + y = 0; + z = -verticalOffset - heightCell; + } else if (upAxis == Direction.WEST) { + x = -verticalOffset - heightCell; + y = 0; + z = horizontalOffset + widthCell; + } else { + x = verticalOffset + heightCell; + y = 0; + z = horizontalOffset + widthCell; + } + return origin.offset(x, y, z); + } + + private Direction resolveUpAxis(LivingEntity shooter, Direction facing) { if (facing.getAxis() != Direction.Axis.Y) { return Direction.UP; } - Direction horizontal = player.getDirection(); + Direction horizontal = shooter.getDirection(); if (horizontal.getAxis() == Direction.Axis.Y) { return Direction.NORTH; } - return horizontal.getOpposite(); + return horizontal; } - private boolean canPlaceAt(Level level, UUID ownerId, PortalGunPortalEntity.PortalSide side, BlockPos supportOrigin, Direction facing, Direction heightAxis, UUID replacingPortalId) { - for (int h = 0; h < 2; h++) { - BlockPos supportPos = supportOrigin.relative(heightAxis, h); - BlockPos airPos = supportPos.relative(facing); - BlockState supportState = level.getBlockState(supportPos); - BlockState airState = level.getBlockState(airPos); - FluidState fluidState = level.getFluidState(airPos); - if (!supportState.isFaceSturdy(level, supportPos, facing)) { - return false; - } - if (!this.isPortalSpaceAvailable(level, airPos, airState, ownerId, side, replacingPortalId)) { - return false; - } - if (!fluidState.isEmpty()) { - return false; - } - } - return true; - } - - private boolean isPortalSpaceAvailable(Level level, BlockPos pos, BlockState state, UUID ownerId, PortalGunPortalEntity.PortalSide side, UUID replacingPortalId) { - if (state.isAir() || state.canBeReplaced()) { - return true; - } + private boolean isPortalSpaceAvailable(Level level, BlockPos pos, BlockState state, UUID ownerId, String channelName, PortalGunPortalEntity.PortalSide side, Direction face) { BlockEntity blockEntity = level.getBlockEntity(pos); - if (blockEntity instanceof PortalGunPortalMasterBlockEntity master) { + if (blockEntity instanceof PortalGunPortalCellAccess portalCell) { + PortalGunPortalFaceRecord record = portalCell.portalGun$getFaceRecord(face); + if (record == null) { + if (blockEntity instanceof PortalGunPortalMasterBlockEntity master && master.getPortalId() != null && master.getFacing() == face) { + return ownerId != null && ownerId.equals(master.getOwnerId()) && channelName.equals(master.getChannelName()) && master.getSide() == side; + } + if (blockEntity instanceof PortalGunPortalBaseBlockEntity base && base.getPortalId() != null) { + BlockEntity masterEntity = level.getBlockEntity(base.getMasterPos()); + if (!(masterEntity instanceof PortalGunPortalMasterBlockEntity master)) { + return false; + } + return master.getFacing() != face + || (ownerId != null + && ownerId.equals(base.getOwnerId()) + && channelName.equals(master.getChannelName()) + && base.getSide() == side + && master.getSide() == side); + } + return true; + } return ownerId != null - && ownerId.equals(master.getOwnerId()) - && master.getSide() == side - && (replacingPortalId == null || replacingPortalId.equals(master.getPortalId())); + && ownerId.equals(record.ownerId()) + && channelName.equals(record.channelName()) + && record.side() == side; } - if (blockEntity instanceof PortalGunPortalBaseBlockEntity base) { - return ownerId != null - && ownerId.equals(base.getOwnerId()) - && base.getSide() == side - && (replacingPortalId == null || replacingPortalId.equals(base.getPortalId())); - } - return false; + return state.getCollisionShape(level, pos).isEmpty(); } - private boolean placePortalBlocks(ServerLevel level, UUID ownerId, PortalGunPortalEntity.PortalSide side, PortalGunPortalEntity portal, PortalGunPlacement placement) { - if (!level.setBlock(placement.masterPos(), this.portalMasterBlock.get().defaultBlockState(), 3)) { - return false; + private boolean placePortalBlocks(ServerLevel level, UUID ownerId, UUID gunId, String ownerIdentity, String channelName, PortalGunPortalEntity.PortalSide side, PortalGunPortalEntity portal, PortalGunPlacement placement) { + int pairTime = level.getServer().getTickCount(); + for (int i = 0; i < placement.portalSpots().length; i++) { + BlockPos spot = placement.portalSpots()[i]; + BlockEntity existing = level.getBlockEntity(spot); + boolean masterRole = i == 0; + if (!(existing instanceof PortalGunPortalCellAccess)) { + BlockState block = masterRole ? this.portalMasterBlock.get().defaultBlockState() : this.portalBaseBlock.get().defaultBlockState(); + if (!level.setBlock(spot, block, 3)) { + for (BlockPos placed : placement.portalSpots()) { + BlockEntity cell = level.getBlockEntity(placed); + if (cell instanceof PortalGunPortalCellAccess portalCell) { + portalCell.portalGun$removeFaceRecord(placement.facing(), portal.getUUID()); + } + } + return false; + } + existing = level.getBlockEntity(spot); + } + if (!(existing instanceof PortalGunPortalCellAccess cell) + || !cell.portalGun$putFaceRecord(new PortalGunPortalFaceRecord( + ownerId, + ownerIdentity, + gunId, + channelName, + portal.getUUID(), + portal.getLinkedPortalId(), + side, + placement.facing(), + placement.upAxis(), + placement.masterPos(), + placement.basePos(), + java.util.Arrays.asList(placement.portalSpots()), + placement.compensatedSpots(), + pairTime, + placement.width(), + placement.height(), + masterRole + ))) { + return false; + } + if (masterRole && existing instanceof PortalGunPortalMasterBlockEntity master + && (master.getPortalId() == null || master.getPortalId().equals(portal.getUUID()))) { + master.configure(ownerId, gunId, ownerIdentity, channelName, portal.getUUID(), side, placement.facing(), placement.upAxis(), placement.basePos(), placement.portalSpots(), placement.compensatedSpots(), pairTime, placement.width(), placement.height()); + } else if (!masterRole && existing instanceof PortalGunPortalBaseBlockEntity base + && (base.getPortalId() == null || base.getPortalId().equals(portal.getUUID()))) { + base.configure(ownerId, portal.getUUID(), side, placement.masterPos()); + } } - if (!level.setBlock(placement.basePos(), this.portalBaseBlock.get().defaultBlockState(), 3)) { - level.setBlock(placement.masterPos(), Blocks.AIR.defaultBlockState(), 3); - return false; - } - if (!(level.getBlockEntity(placement.masterPos()) instanceof PortalGunPortalMasterBlockEntity master) || !(level.getBlockEntity(placement.basePos()) instanceof PortalGunPortalBaseBlockEntity base)) { - level.setBlock(placement.masterPos(), Blocks.AIR.defaultBlockState(), 3); - level.setBlock(placement.basePos(), Blocks.AIR.defaultBlockState(), 3); - return false; - } - master.configure(ownerId, portal.getUUID(), side, placement.facing(), placement.upAxis(), placement.basePos(), placement.portalSpots(), placement.compensatedSpots(), level.getServer().getTickCount()); - base.configure(ownerId, portal.getUUID(), side, placement.masterPos()); return true; } private void syncPortalBlocks(ServerLevel level, PortalGunPortalEntity portal) { - if (level.getBlockEntity(portal.getMasterPos()) instanceof PortalGunPortalMasterBlockEntity master) { - master.updatePair(portal.getLinkedPortalId(), portal.getPairTime()); - } - if (level.getBlockEntity(portal.getBasePos()) instanceof PortalGunPortalBaseBlockEntity base) { - base.updatePair(portal.getLinkedPortalId(), portal.getPairTime()); + for (BlockPos portalSpot : portal.getPortalSpots()) { + if (level.getBlockEntity(portalSpot) instanceof PortalGunPortalCellAccess cell) { + cell.portalGun$updateFacePair(portal.getUUID(), portal.getLinkedPortalId(), portal.getPairTime()); + } + if (portalSpot.equals(portal.getMasterPos()) && level.getBlockEntity(portalSpot) instanceof PortalGunPortalMasterBlockEntity master && portal.getUUID().equals(master.getPortalId())) { + master.updatePair(portal.getLinkedPortalId(), portal.getPairTime()); + } else if (!portalSpot.equals(portal.getMasterPos()) && level.getBlockEntity(portalSpot) instanceof PortalGunPortalBaseBlockEntity base && portal.getUUID().equals(base.getPortalId())) { + base.updatePair(portal.getLinkedPortalId(), portal.getPairTime()); + } } } @@ -452,16 +640,115 @@ public class PortalGunItem extends Item implements GeoItem { return side.name().equals(sideName); } + public static UUID getGunId(ItemStack stack) { + net.minecraft.world.item.component.CustomData customData = stack.get(DataComponents.CUSTOM_DATA); + if (customData == null || !customData.copyTag().hasUUID(GUN_ID_TAG)) { + return null; + } + return customData.copyTag().getUUID(GUN_ID_TAG); + } + private UUID ensureGunId(ItemStack stack) { net.minecraft.world.item.component.CustomData customData = stack.get(DataComponents.CUSTOM_DATA); + UUID gunId; if (customData != null && customData.copyTag().hasUUID(GUN_ID_TAG)) { - return customData.copyTag().getUUID(GUN_ID_TAG); + gunId = customData.copyTag().getUUID(GUN_ID_TAG); + } else { + gunId = UUID.randomUUID(); } - UUID gunId = UUID.randomUUID(); - net.minecraft.world.item.component.CustomData.update(DataComponents.CUSTOM_DATA, stack, tag -> tag.putUUID(GUN_ID_TAG, gunId)); + String channelName = "Random Channel #" + gunId.hashCode(); + net.minecraft.world.item.component.CustomData.update(DataComponents.CUSTOM_DATA, stack, tag -> { + tag.putUUID(GUN_ID_TAG, gunId); + if (!tag.contains(CHANNEL_NAME_TAG)) { + tag.putString(CHANNEL_NAME_TAG, channelName); + } + if (!tag.contains(LAST_SIDE_TAG)) { + tag.putString(LAST_SIDE_TAG, PortalGunPortalEntity.PortalSide.BLUE.name()); + } + }); return gunId; } + private UUID ensureGunId(ItemStack stack, UUID ownerId, String ownerName) { + UUID gunId = this.ensureGunId(stack); + net.minecraft.world.item.component.CustomData.update(DataComponents.CUSTOM_DATA, stack, tag -> { + if (!tag.contains(OWNER_ID_TAG)) { + tag.putString(OWNER_ID_TAG, ownerId.toString()); + tag.putString(OWNER_NAME_TAG, ownerName); + } + }); + return gunId; + } + + public static String getChannelName(ItemStack stack) { + net.minecraft.world.item.component.CustomData customData = stack.get(DataComponents.CUSTOM_DATA); + if (customData == null) { + return ""; + } + return customData.copyTag().getString(CHANNEL_NAME_TAG); + } + + public static String getPortalOwnerName(ItemStack stack) { + net.minecraft.world.item.component.CustomData customData = stack.get(DataComponents.CUSTOM_DATA); + return customData == null ? "" : customData.copyTag().getString(OWNER_NAME_TAG); + } + + public static UUID ensureGunIdentity(ItemStack stack) { + if (stack.isEmpty() || !(stack.getItem() instanceof PortalGunItem item)) { + return null; + } + return item.ensureGunId(stack); + } + + public static UUID ensureGunIdentity(ItemStack stack, Player owner) { + if (stack.isEmpty() || !(stack.getItem() instanceof PortalGunItem item)) { + return null; + } + return item.ensureGunId(stack, owner.getUUID(), owner.getGameProfile().getName()); + } + + public static UUID getPortalOwnerId(ItemStack stack, UUID fallback) { + net.minecraft.world.item.component.CustomData customData = stack.get(DataComponents.CUSTOM_DATA); + if (customData == null) { + return fallback; + } + String owner = customData.copyTag().getString(OWNER_ID_TAG); + if ("Global".equals(owner)) { + return GLOBAL_OWNER_ID; + } + try { + return owner.isEmpty() ? fallback : UUID.fromString(owner); + } catch (IllegalArgumentException ignored) { + return fallback; + } + } + + public static String getPortalOwnerIdentity(ItemStack stack, UUID fallback) { + net.minecraft.world.item.component.CustomData customData = stack.get(DataComponents.CUSTOM_DATA); + if (customData == null) { + return String.valueOf(fallback); + } + String owner = customData.copyTag().getString(OWNER_ID_TAG); + return owner.isEmpty() ? String.valueOf(fallback) : owner; + } + + public static ItemStack createGlobalVariant(Item item, String channelName) { + ItemStack stack = new ItemStack(item); + UUID gunId = UUID.nameUUIDFromBytes(("Global\u0000" + channelName).getBytes(StandardCharsets.UTF_8)); + net.minecraft.world.item.component.CustomData.update(DataComponents.CUSTOM_DATA, stack, tag -> { + tag.putUUID(GUN_ID_TAG, gunId); + tag.putString(OWNER_ID_TAG, "Global"); + tag.putString(OWNER_NAME_TAG, "Global"); + tag.putString(CHANNEL_NAME_TAG, channelName); + tag.putInt(PORTAL_WIDTH_TAG, 1); + tag.putInt(PORTAL_HEIGHT_TAG, 2); + tag.putInt(GRAB_STRENGTH_TAG, 4); + tag.putString(LAST_SIDE_TAG, PortalGunPortalEntity.PortalSide.BLUE.name()); + tag.putBoolean("lastFired", true); + }); + return stack; + } + private boolean matchesGunId(ItemStack stack, UUID gunId) { if (stack.isEmpty() || stack.getItem() != this) { return false; @@ -484,7 +771,17 @@ public class PortalGunItem extends Item implements GeoItem { public void appendHoverText(ItemStack stack, Item.TooltipContext context, List tooltipComponents, TooltipFlag tooltipFlag) { tooltipComponents.add(Component.translatable("tooltip.antarchy.portal_gun.primary").withStyle(ChatFormatting.GRAY)); tooltipComponents.add(Component.translatable("tooltip.antarchy.portal_gun.secondary").withStyle(ChatFormatting.GRAY)); + String ownerName = getPortalOwnerName(stack); + if (!ownerName.isEmpty()) { + tooltipComponents.add(Component.translatable("tooltip.antarchy.portal_gun.owner", ownerName).withStyle(ChatFormatting.GRAY)); + } + String channelName = getChannelName(stack); + if (!channelName.isEmpty()) { + tooltipComponents.add(Component.translatable("tooltip.antarchy.portal_gun.channel", channelName).withStyle(ChatFormatting.GRAY)); + } tooltipComponents.add(Component.translatable("tooltip.antarchy.portal_gun.reset").withStyle(ChatFormatting.DARK_GRAY)); + tooltipComponents.add(Component.translatable("tooltip.antarchy.portal_gun.size", getPortalWidth(stack), getPortalHeight(stack)).withStyle(ChatFormatting.GRAY)); + tooltipComponents.add(Component.translatable("tooltip.antarchy.portal_gun.grab_strength", getGrabStrength(stack)).withStyle(ChatFormatting.GRAY)); } @Override diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/PortalGunGrabPayload.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/PortalGunGrabPayload.java new file mode 100644 index 00000000..410ba1c3 --- /dev/null +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/PortalGunGrabPayload.java @@ -0,0 +1,17 @@ +package com.craisinlord.antarchy.content.network; + +import com.craisinlord.antarchy.Antarchy; +import io.netty.buffer.ByteBuf; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import net.minecraft.resources.ResourceLocation; + +public record PortalGunGrabPayload() implements CustomPacketPayload { + public static final Type TYPE = new Type<>(ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "portal_gun_grab")); + public static final StreamCodec STREAM_CODEC = StreamCodec.unit(new PortalGunGrabPayload()); + + @Override + public Type type() { + return TYPE; + } +} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/PortalGunGrabStatePayload.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/PortalGunGrabStatePayload.java new file mode 100644 index 00000000..04097b63 --- /dev/null +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/PortalGunGrabStatePayload.java @@ -0,0 +1,20 @@ +package com.craisinlord.antarchy.content.network; + +import com.craisinlord.antarchy.Antarchy; +import io.netty.buffer.ByteBuf; +import net.minecraft.network.codec.ByteBufCodecs; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import net.minecraft.resources.ResourceLocation; + +public record PortalGunGrabStatePayload(boolean active) implements CustomPacketPayload { + public static final Type TYPE = new Type<>(ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "portal_gun_grab_state")); + public static final StreamCodec STREAM_CODEC = StreamCodec.composite( + ByteBufCodecs.BOOL, PortalGunGrabStatePayload::active, PortalGunGrabStatePayload::new + ); + + @Override + public Type type() { + return TYPE; + } +} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/PortalGunIndicatorPayload.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/PortalGunIndicatorPayload.java new file mode 100644 index 00000000..684e1831 --- /dev/null +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/PortalGunIndicatorPayload.java @@ -0,0 +1,26 @@ +package com.craisinlord.antarchy.content.network; + +import com.craisinlord.antarchy.Antarchy; +import io.netty.buffer.ByteBuf; +import java.util.UUID; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import net.minecraft.resources.ResourceLocation; + +public record PortalGunIndicatorPayload(UUID gunId, boolean blueAvailable, boolean orangeAvailable) implements CustomPacketPayload { + public static final Type TYPE = new Type<>(ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "portal_gun_indicator")); + public static final StreamCodec STREAM_CODEC = StreamCodec.of( + (buffer, payload) -> { + buffer.writeLong(payload.gunId().getMostSignificantBits()); + buffer.writeLong(payload.gunId().getLeastSignificantBits()); + buffer.writeBoolean(payload.blueAvailable()); + buffer.writeBoolean(payload.orangeAvailable()); + }, + buffer -> new PortalGunIndicatorPayload(new UUID(buffer.readLong(), buffer.readLong()), buffer.readBoolean(), buffer.readBoolean()) + ); + + @Override + public Type type() { + return TYPE; + } +} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/PortalGunIndicatorRequestPayload.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/PortalGunIndicatorRequestPayload.java new file mode 100644 index 00000000..6d16cb9a --- /dev/null +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/PortalGunIndicatorRequestPayload.java @@ -0,0 +1,24 @@ +package com.craisinlord.antarchy.content.network; + +import com.craisinlord.antarchy.Antarchy; +import io.netty.buffer.ByteBuf; +import java.util.UUID; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import net.minecraft.resources.ResourceLocation; + +public record PortalGunIndicatorRequestPayload(UUID gunId) implements CustomPacketPayload { + public static final Type TYPE = new Type<>(ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "portal_gun_indicator_request")); + public static final StreamCodec STREAM_CODEC = StreamCodec.of( + (buffer, payload) -> { + buffer.writeLong(payload.gunId().getMostSignificantBits()); + buffer.writeLong(payload.gunId().getLeastSignificantBits()); + }, + buffer -> new PortalGunIndicatorRequestPayload(new UUID(buffer.readLong(), buffer.readLong())) + ); + + @Override + public Type type() { + return TYPE; + } +} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/PortalGunPrimaryPayload.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/PortalGunPrimaryPayload.java index e466a550..fa21e897 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/PortalGunPrimaryPayload.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/PortalGunPrimaryPayload.java @@ -2,13 +2,17 @@ package com.craisinlord.antarchy.content.network; import com.craisinlord.antarchy.Antarchy; import io.netty.buffer.ByteBuf; +import net.minecraft.network.codec.ByteBufCodecs; import net.minecraft.network.codec.StreamCodec; import net.minecraft.network.protocol.common.custom.CustomPacketPayload; import net.minecraft.resources.ResourceLocation; -public record PortalGunPrimaryPayload() implements CustomPacketPayload { +public record PortalGunPrimaryPayload(boolean offhand) implements CustomPacketPayload { public static final Type TYPE = new Type<>(ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "portal_gun_primary")); - public static final StreamCodec STREAM_CODEC = StreamCodec.unit(new PortalGunPrimaryPayload()); + public static final StreamCodec STREAM_CODEC = StreamCodec.composite( + ByteBufCodecs.BOOL, PortalGunPrimaryPayload::offhand, + PortalGunPrimaryPayload::new + ); @Override public Type type() { diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/PortalGunResetPayload.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/PortalGunResetPayload.java new file mode 100644 index 00000000..4e1aef8b --- /dev/null +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/PortalGunResetPayload.java @@ -0,0 +1,20 @@ +package com.craisinlord.antarchy.content.network; + +import com.craisinlord.antarchy.Antarchy; +import io.netty.buffer.ByteBuf; +import net.minecraft.network.codec.ByteBufCodecs; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import net.minecraft.resources.ResourceLocation; + +public record PortalGunResetPayload(int action) implements CustomPacketPayload { + public static final Type TYPE = new Type<>(ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "portal_gun_reset")); + public static final StreamCodec STREAM_CODEC = StreamCodec.composite( + ByteBufCodecs.VAR_INT, PortalGunResetPayload::action, PortalGunResetPayload::new + ); + + @Override + public Type type() { + return TYPE; + } +} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/portalgun/PortalGunBlackHoleEntity.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/portalgun/PortalGunBlackHoleEntity.java index 9be64f20..33b326e4 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/portalgun/PortalGunBlackHoleEntity.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/portalgun/PortalGunBlackHoleEntity.java @@ -4,7 +4,6 @@ import java.util.List; import java.util.UUID; import net.minecraft.nbt.CompoundTag; import net.minecraft.server.level.ServerLevel; -import net.minecraft.sounds.SoundEvents; import net.minecraft.sounds.SoundSource; import net.minecraft.world.damagesource.DamageSource; import net.minecraft.world.entity.Entity; @@ -73,7 +72,7 @@ public class PortalGunBlackHoleEntity extends Entity implements GeoEntity { } this.pullNearby(serverLevel); if (this.ageTicks % 10 == 0) { - serverLevel.playSound(null, this.blockPosition(), SoundEvents.PORTAL_AMBIENT, SoundSource.HOSTILE, 0.5F, 0.6F); + serverLevel.playSound(null, this.blockPosition(), PortalGunPortalEntity.sound("portal_ambient"), SoundSource.HOSTILE, 0.5F, 0.6F); } } diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/portalgun/PortalGunCollisionHelper.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/portalgun/PortalGunCollisionHelper.java index e659d73b..63ffe32b 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/portalgun/PortalGunCollisionHelper.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/portalgun/PortalGunCollisionHelper.java @@ -23,13 +23,12 @@ public final class PortalGunCollisionHelper { return collided; } AABB pathBox = startBox.expandTowards(movement).inflate(SEARCH_PADDING); - List nearby = entity.level().getEntities(entity, pathBox, candidate -> candidate instanceof PortalGunPortalEntity && candidate.isAlive()); + List nearby = PortalGunPortalEntity.findPortalsNearBounds(entity.level(), pathBox); if (nearby.isEmpty()) { return collided; } Vec3 adjusted = collided; - for (Entity candidate : nearby) { - PortalGunPortalEntity portal = (PortalGunPortalEntity) candidate; + for (PortalGunPortalEntity portal : nearby) { Vec3 candidateAdjusted = resolveCollisionAgainstPortal(entity, startBox, movement, adjusted, portal); if (candidateAdjusted.distanceToSqr(movement) < adjusted.distanceToSqr(movement)) { adjusted = candidateAdjusted; @@ -39,6 +38,10 @@ public final class PortalGunCollisionHelper { } private static Vec3 resolveCollisionAgainstPortal(Entity entity, AABB startBox, Vec3 movement, Vec3 collided, PortalGunPortalEntity portal) { + PortalGunPortalEntity linked = portal.getLinkedPortal(); + if (linked == null || !linked.isAlive() || portal.isTeleportCoolingDown(entity)) { + return collided; + } PortalGunWorldPortalShape shape = portal.getWorldPortalShape(); Vec3 normal = portal.getNormalVec().normalize(); double desiredNormal = movement.dot(normal); @@ -60,21 +63,21 @@ public final class PortalGunCollisionHelper { && !portal.getPortalInsides(entity).intersects(endBox) && !intersectsPortalWindow(startBox, shape) && !intersectsPortalWindow(endBox, shape) - && !segmentIntersectsPortalWindow(shape, startBox, endBox)) { + && !segmentIntersectsPortalWindow(entity, portal, startBox, endBox)) { return false; } - if (crossesPortal(shape, entity, startBox, movement)) { + Vec3 startProbe = portal.teleportProbePosition(entity, startBox); + Vec3 endProbe = portal.teleportProbePosition(entity, endBox); + if (portal.crossesPortal(startProbe, endProbe)) { return true; } - Vec3 startCenter = startBox.getCenter(); - Vec3 endCenter = endBox.getCenter(); - PortalGunWorldPortalShape.PortalLocalCoords startCoords = shape.localCoords(startCenter); - PortalGunWorldPortalShape.PortalLocalCoords endCoords = shape.localCoords(endCenter); + PortalGunWorldPortalShape.PortalLocalCoords startCoords = shape.localCoords(startProbe); + PortalGunWorldPortalShape.PortalLocalCoords endCoords = shape.localCoords(endProbe); return startCoords.depth() > 0.0D && endCoords.depth() >= -PLANE_TOLERANCE && Math.abs(endCoords.horizontal()) <= shape.halfWidth() + EDGE_PADDING && Math.abs(endCoords.vertical()) <= shape.halfHeight() + EDGE_PADDING - && projectedWindowOverlap(shape, startBox, endBox); + && projectedWindowOverlap(shape, startProbe, endProbe); } private static boolean intersectsPortalWindow(AABB box, PortalGunWorldPortalShape shape) { @@ -90,39 +93,17 @@ public final class PortalGunCollisionHelper { return false; } - private static boolean segmentIntersectsPortalWindow(PortalGunWorldPortalShape shape, AABB startBox, AABB endBox) { - for (Vec3 startCorner : corners(startBox)) { - Vec3 endCorner = matchSample(startCorner, startBox, endBox); - if (shape.crosses(startCorner, endCorner)) { - return true; - } - } - return false; + private static boolean segmentIntersectsPortalWindow(Entity entity, PortalGunPortalEntity portal, AABB startBox, AABB endBox) { + return portal.crossesPortal(portal.teleportProbePosition(entity, startBox), portal.teleportProbePosition(entity, endBox)); } - private static boolean projectedWindowOverlap(PortalGunWorldPortalShape shape, AABB startBox, AABB endBox) { - double minHorizontal = Double.POSITIVE_INFINITY; - double maxHorizontal = Double.NEGATIVE_INFINITY; - double minVertical = Double.POSITIVE_INFINITY; - double maxVertical = Double.NEGATIVE_INFINITY; - for (Vec3 corner : corners(startBox)) { - PortalGunWorldPortalShape.PortalLocalCoords coords = shape.localCoords(corner); - minHorizontal = Math.min(minHorizontal, coords.horizontal()); - maxHorizontal = Math.max(maxHorizontal, coords.horizontal()); - minVertical = Math.min(minVertical, coords.vertical()); - maxVertical = Math.max(maxVertical, coords.vertical()); - } - for (Vec3 corner : corners(endBox)) { - PortalGunWorldPortalShape.PortalLocalCoords coords = shape.localCoords(corner); - minHorizontal = Math.min(minHorizontal, coords.horizontal()); - maxHorizontal = Math.max(maxHorizontal, coords.horizontal()); - minVertical = Math.min(minVertical, coords.vertical()); - maxVertical = Math.max(maxVertical, coords.vertical()); - } - return maxHorizontal >= -shape.halfWidth() - BORDER_TOLERANCE - && minHorizontal <= shape.halfWidth() + BORDER_TOLERANCE - && maxVertical >= -shape.halfHeight() - BORDER_TOLERANCE - && minVertical <= shape.halfHeight() + BORDER_TOLERANCE; + private static boolean projectedWindowOverlap(PortalGunWorldPortalShape shape, Vec3 startProbe, Vec3 endProbe) { + PortalGunWorldPortalShape.PortalLocalCoords start = shape.localCoords(startProbe); + PortalGunWorldPortalShape.PortalLocalCoords end = shape.localCoords(endProbe); + return Math.abs(start.horizontal()) <= shape.halfWidth() + BORDER_TOLERANCE + && Math.abs(start.vertical()) <= shape.halfHeight() + BORDER_TOLERANCE + && Math.abs(end.horizontal()) <= shape.halfWidth() + BORDER_TOLERANCE + && Math.abs(end.vertical()) <= shape.halfHeight() + BORDER_TOLERANCE; } private static Vec3 applyBorderCollision(Entity entity, AABB startBox, Vec3 movement, PortalGunPortalEntity portal) { @@ -143,51 +124,6 @@ public final class PortalGunCollisionHelper { return collided.distanceToSqr(movement) < 1.0E-7D ? movement : collided; } - private static boolean crossesPortal(PortalGunWorldPortalShape shape, Entity entity, AABB startBox, Vec3 movement) { - AABB endBox = startBox.move(movement); - for (Vec3 startSample : portalSamples(entity, startBox, true)) { - Vec3 endSample = matchSample(startSample, startBox, endBox); - if (shape.crosses(startSample, endSample)) { - return true; - } - } - return false; - } - - private static List portalSamples(Entity entity, AABB box, boolean includeEye) { - Vec3 center = new Vec3((box.minX + box.maxX) * 0.5D, (box.minY + box.maxY) * 0.5D, (box.minZ + box.maxZ) * 0.5D); - Vec3 topCenter = new Vec3(center.x, box.maxY, center.z); - Vec3 bottomCenter = new Vec3(center.x, box.minY, center.z); - if (includeEye) { - Vec3 eye = new Vec3(entity.getX(), entity.getY() + entity.getEyeHeight(), entity.getZ()); - return List.of(center, eye, topCenter, bottomCenter); - } - return List.of(center, topCenter, bottomCenter); - } - - private static Vec3 matchSample(Vec3 startSample, AABB startBox, AABB endBox) { - double xLerp = axisRatio(startSample.x, startBox.minX, startBox.maxX); - double yLerp = axisRatio(startSample.y, startBox.minY, startBox.maxY); - double zLerp = axisRatio(startSample.z, startBox.minZ, startBox.maxZ); - return new Vec3( - lerp(endBox.minX, endBox.maxX, xLerp), - lerp(endBox.minY, endBox.maxY, yLerp), - lerp(endBox.minZ, endBox.maxZ, zLerp) - ); - } - - private static double axisRatio(double value, double min, double max) { - double size = max - min; - if (Math.abs(size) < 1.0E-6D) { - return 0.5D; - } - return (value - min) / size; - } - - private static double lerp(double min, double max, double delta) { - return min + (max - min) * delta; - } - private static Vec3[] corners(AABB box) { return new Vec3[] { new Vec3(box.minX, box.minY, box.minZ), diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/portalgun/PortalGunGrabManager.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/portalgun/PortalGunGrabManager.java new file mode 100644 index 00000000..084a842d --- /dev/null +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/portalgun/PortalGunGrabManager.java @@ -0,0 +1,280 @@ +package com.craisinlord.antarchy.content.portalgun; + +import com.craisinlord.antarchy.content.item.PortalGunItem; +import com.craisinlord.antarchy.content.AntarchyTags; +import com.craisinlord.antarchy.Antarchy; +import com.craisinlord.antarchy.config.AntarchySettings; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.core.BlockPos; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.world.level.block.state.properties.BlockStateProperties; +import net.minecraft.world.level.block.state.properties.DoubleBlockHalf; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.entity.item.FallingBlockEntity; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.MoverType; +import net.minecraft.world.entity.projectile.AbstractHurtingProjectile; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.tags.BlockTags; +import net.minecraft.world.level.block.RenderShape; +import net.minecraft.world.level.ClipContext; +import net.minecraft.world.phys.AABB; +import net.minecraft.world.phys.BlockHitResult; +import net.minecraft.world.phys.EntityHitResult; +import net.minecraft.world.phys.HitResult; +import net.minecraft.world.phys.Vec3; +import java.util.function.BiConsumer; +import com.craisinlord.antarchy.content.network.PortalGunGrabStatePayload; + +public final class PortalGunGrabManager { + private static final double REACH = 5.0D; + private static final double BASE_HOLD_DISTANCE = 3.5D; + private static final double MAX_HOLD_DISTANCE = 6.0D; + private static final Map GRABS = new HashMap<>(); + private static BiConsumer stateSender = (player, payload) -> {}; + + private PortalGunGrabManager() { + } + + public static void setStateSender(BiConsumer sender) { + stateSender = sender; + } + + public static void toggle(ServerPlayer player) { + GrabState active = GRABS.remove(player.getUUID()); + if (active != null) { + release(active); + stateSender.accept(player, new PortalGunGrabStatePayload(false)); + return; + } + ItemStack gunStack = getHeldPortalGun(player); + if (gunStack.isEmpty()) { + return; + } + int grabStrength = PortalGunItem.getGrabStrength(gunStack); + Entity target = findTarget(player, grabStrength); + if (target == null) { + target = findBlockTarget(player, grabStrength); + if (target == null) { + return; + } + } + for (Map.Entry entry : new HashMap<>(GRABS).entrySet()) { + if (entry.getValue().target() == target) { + GRABS.remove(entry.getKey()); + release(entry.getValue()); + ServerPlayer previousHolder = player.serverLevel().getServer().getPlayerList().getPlayer(entry.getKey()); + if (previousHolder != null) { + stateSender.accept(previousHolder, new PortalGunGrabStatePayload(false)); + } + } + } + double holdDistance = BASE_HOLD_DISTANCE; + if (target.getBbWidth() > 2.0F) { + holdDistance = Math.min(MAX_HOLD_DISTANCE, holdDistance + target.getBbWidth() - 2.0D); + } + GrabState state = new GrabState(player.getUUID(), player.serverLevel(), target, holdDistance, target.isNoGravity()); + GRABS.put(player.getUUID(), state); + target.setNoGravity(true); + stateSender.accept(player, new PortalGunGrabStatePayload(true)); + } + + public static void tick(MinecraftServer server) { + for (Map.Entry entry : new HashMap<>(GRABS).entrySet()) { + GrabState state = entry.getValue(); + ServerPlayer player = server.getPlayerList().getPlayer(state.playerId()); + Entity target = state.target(); + if (player == null || !player.isAlive() || player.serverLevel() != state.level() || !isHoldingPortalGun(player) + || !target.isAlive() || target.isRemoved() || target.level() != state.level() + || target.distanceTo(player) > state.holdDistance() + 6.25D) { + GRABS.remove(entry.getKey()); + release(state); + if (player != null) { + stateSender.accept(player, new PortalGunGrabStatePayload(false)); + } + continue; + } + Vec3 holdPosition = player.getEyePosition(1.0F).add(player.getLookAngle().scale(state.holdDistance())); + if (target.position().distanceTo(holdPosition) > 5.0D) { + target.teleportTo(player.getX(), player.getY(), player.getZ()); + } + Vec3 velocity = holdPosition.subtract(target.getX(), target.getBoundingBox().getCenter().y, target.getZ()); + target.setDeltaMovement(velocity); + target.move(MoverType.SELF, velocity); + velocity = holdPosition.subtract(target.getX(), target.getBoundingBox().getCenter().y, target.getZ()); + target.setDeltaMovement(velocity); + target.setNoGravity(true); + if (target instanceof FallingBlockEntity fallingBlock) { + fallingBlock.time = 2; + } + if (target instanceof AbstractHurtingProjectile projectile) { + projectile.accelerationPower = 0.0D; + } + target.fallDistance = (float) -(velocity.y * velocity.y); + target.setOnGround(false); + target.hasImpulse = true; + target.hurtMarked = true; + } + } + + public static void clear(MinecraftServer server) { + for (GrabState state : GRABS.values()) { + release(state); + ServerPlayer player = server.getPlayerList().getPlayer(state.playerId()); + if (player != null) { + stateSender.accept(player, new PortalGunGrabStatePayload(false)); + } + } + GRABS.clear(); + } + + private static Entity findTarget(ServerPlayer player, int grabStrength) { + Vec3 start = player.getEyePosition(); + Vec3 look = player.getLookAngle(); + Vec3 end = start.add(look.scale(REACH)); + BlockHitResult blockHit = player.serverLevel().clip(new ClipContext(start, end, ClipContext.Block.COLLIDER, ClipContext.Fluid.NONE, player)); + double maxDistance = blockHit.getType() == HitResult.Type.BLOCK ? start.distanceTo(blockHit.getLocation()) : REACH; + AABB searchBounds = player.getBoundingBox().expandTowards(look.scale(maxDistance)).inflate(1.0D); + EntityHitResult closest = null; + double closestDistance = maxDistance * maxDistance; + for (Entity candidate : player.serverLevel().getEntities(player, searchBounds, entity -> canGrab(entity, grabStrength))) { + var hit = candidate.getBoundingBox().inflate(candidate.getPickRadius()).clip(start, end); + if (hit.isEmpty()) { + continue; + } + double distance = start.distanceToSqr(hit.get()); + if (distance < closestDistance) { + closestDistance = distance; + closest = new EntityHitResult(candidate, hit.get()); + } + } + return closest == null ? null : closest.getEntity(); + } + + private static Entity findBlockTarget(ServerPlayer player, int grabStrength) { + if (grabStrength < 2) { + return null; + } + Vec3 start = player.getEyePosition(); + Vec3 end = start.add(player.getLookAngle().scale(REACH)); + BlockHitResult hit = player.serverLevel().clip(new ClipContext(start, end, ClipContext.Block.OUTLINE, ClipContext.Fluid.NONE, player)); + if (hit.getType() != HitResult.Type.BLOCK) { + return null; + } + BlockPos pos = hit.getBlockPos(); + ServerLevel level = player.serverLevel(); + BlockState state = level.getBlockState(pos); + if (state.isAir() || state.is(AntarchyTags.Blocks.GRAVITY_GUN_BLACKLIST)) { + return null; + } + BlockPos basePos = pos; + BlockPos upperPos = null; + if (state.hasProperty(BlockStateProperties.DOUBLE_BLOCK_HALF)) { + basePos = state.getValue(BlockStateProperties.DOUBLE_BLOCK_HALF) == DoubleBlockHalf.UPPER ? pos.below() : pos; + BlockState baseState = level.getBlockState(basePos); + BlockPos candidateUpper = basePos.above(); + BlockState upperState = level.getBlockState(candidateUpper); + if (!baseState.is(state.getBlock()) || !baseState.hasProperty(BlockStateProperties.DOUBLE_BLOCK_HALF) + || baseState.getValue(BlockStateProperties.DOUBLE_BLOCK_HALF) != DoubleBlockHalf.LOWER + || !upperState.is(state.getBlock()) || !upperState.hasProperty(BlockStateProperties.DOUBLE_BLOCK_HALF) + || upperState.getValue(BlockStateProperties.DOUBLE_BLOCK_HALF) != DoubleBlockHalf.UPPER) { + return null; + } + state = baseState; + upperPos = candidateUpper; + } + if (state.getDestroySpeed(level, basePos) < 0.0F) { + return null; + } + if (AntarchySettings.portalGunEntityGrabWeightBase() == 0 + && !Antarchy.MODID.equals(BuiltInRegistries.BLOCK.getKey(state.getBlock()).getNamespace())) { + return null; + } + BlockEntity blockEntity = level.getBlockEntity(basePos); + if (blockEntity != null && grabStrength < 3) { + return null; + } + if (!state.isSolidRender(level, basePos) + && state.getRenderShape() != RenderShape.ENTITYBLOCK_ANIMATED + && !state.is(Blocks.SNOW) + && !state.is(Blocks.GLOWSTONE) + && !state.is(Blocks.CAKE) + && !state.is(Blocks.TNT) + && !state.is(Blocks.ICE) + && !state.is(BlockTags.SLABS) + && !state.is(Blocks.ANVIL) + && !state.is(BlockTags.STAIRS)) { + return null; + } + CompoundTag blockEntityData = blockEntity == null ? null : blockEntity.saveWithId(level.registryAccess()); + if (blockEntity != null) { + level.removeBlockEntity(basePos); + } + if (upperPos != null) { + level.removeBlock(upperPos, false); + } + FallingBlockEntity fallingBlock = FallingBlockEntity.fall(level, basePos, state); + if (fallingBlock == null) { + return null; + } + if (blockEntityData != null) { + fallingBlock.blockData = blockEntityData; + } + fallingBlock.setDeltaMovement(Vec3.ZERO); + fallingBlock.setNoGravity(true); + fallingBlock.hurtMarked = true; + return fallingBlock; + } + + private static boolean canGrab(Entity entity, int grabStrength) { + if (!entity.isAlive() || entity instanceof Player || entity instanceof PortalGunPortalEntity || entity.isPassenger()) { + return false; + } + double volume = entity.getBbWidth() * entity.getBbHeight() * entity.getBbWidth(); + double strengthScale = switch (grabStrength) { + case 1 -> 1.0D; + case 2 -> 1.5D; + case 3 -> 2.25D; + default -> 3.0D; + }; + int grabWeightBase = AntarchySettings.portalGunEntityGrabWeightBase(); + boolean modEntityExempt = grabWeightBase == 0 + && Antarchy.MODID.equals(BuiltInRegistries.ENTITY_TYPE.getKey(entity.getType()).getNamespace()); + return (modEntityExempt || Math.cbrt(volume) < grabWeightBase * strengthScale / 100.0D) && entity.isPickable(); + } + + private static ItemStack getHeldPortalGun(ServerPlayer player) { + if (player.getMainHandItem().getItem() instanceof PortalGunItem) { + return player.getMainHandItem(); + } + if (player.getOffhandItem().getItem() instanceof PortalGunItem) { + return player.getOffhandItem(); + } + return ItemStack.EMPTY; + } + + private static boolean isHoldingPortalGun(ServerPlayer player) { + return !getHeldPortalGun(player).isEmpty(); + } + + private static void release(GrabState state) { + Entity target = state.target(); + if (target.isAlive() && !target.isRemoved()) { + target.setNoGravity(state.hadNoGravity()); + target.hasImpulse = true; + target.hurtMarked = true; + } + } + + private record GrabState(UUID playerId, ServerLevel level, Entity target, double holdDistance, boolean hadNoGravity) { + } +} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/portalgun/PortalGunIndicatorSync.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/portalgun/PortalGunIndicatorSync.java new file mode 100644 index 00000000..84e94568 --- /dev/null +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/portalgun/PortalGunIndicatorSync.java @@ -0,0 +1,49 @@ +package com.craisinlord.antarchy.content.portalgun; + +import com.craisinlord.antarchy.content.item.PortalGunItem; +import com.craisinlord.antarchy.content.network.PortalGunIndicatorPayload; +import com.craisinlord.antarchy.content.network.PortalGunIndicatorRequestPayload; +import java.util.UUID; +import java.util.function.BiConsumer; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.item.ItemStack; + +public final class PortalGunIndicatorSync { + private static BiConsumer sender = (player, payload) -> {}; + + private PortalGunIndicatorSync() { + } + + public static void setSender(BiConsumer payloadSender) { + sender = payloadSender; + } + + public static void handleRequest(ServerPlayer player, PortalGunIndicatorRequestPayload request) { + ItemStack main = player.getMainHandItem(); + ItemStack off = player.getOffhandItem(); + ItemStack stack = main.getItem() instanceof PortalGunItem && request.gunId().equals(PortalGunItem.getGunId(main)) + ? main + : off.getItem() instanceof PortalGunItem && request.gunId().equals(PortalGunItem.getGunId(off)) ? off : ItemStack.EMPTY; + if (stack.isEmpty()) { + return; + } + UUID ownerId = PortalGunItem.getPortalOwnerId(stack, player.getUUID()); + boolean blue = PortalGunSavedData.getPortalId( + player.serverLevel().getServer(), + ownerId, + request.gunId(), + PortalGunItem.getChannelName(stack), + PortalGunPortalEntity.PortalSide.BLUE, + player.serverLevel().dimension().location() + ).isPresent(); + boolean orange = PortalGunSavedData.getPortalId( + player.serverLevel().getServer(), + ownerId, + request.gunId(), + PortalGunItem.getChannelName(stack), + PortalGunPortalEntity.PortalSide.ORANGE, + player.serverLevel().dimension().location() + ).isPresent(); + sender.accept(player, new PortalGunIndicatorPayload(request.gunId(), blue, orange)); + } +} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/portalgun/PortalGunPlacement.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/portalgun/PortalGunPlacement.java index 944f4291..36ace529 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/portalgun/PortalGunPlacement.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/portalgun/PortalGunPlacement.java @@ -1,6 +1,7 @@ package com.craisinlord.antarchy.content.portalgun; import java.util.Set; +import java.util.HashSet; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.world.phys.Vec3; @@ -15,13 +16,18 @@ public record PortalGunPlacement( BlockPos masterPos, BlockPos basePos, BlockPos[] portalSpots, - Set compensatedSpots + Set compensatedSpots, + int width, + int height ) { - public static PortalGunPlacement fromStored(Direction facing, Direction upAxis, BlockPos masterPos, BlockPos basePos, Set compensatedSpots) { + public static PortalGunPlacement fromStored(Direction facing, Direction upAxis, BlockPos masterPos, BlockPos basePos, BlockPos[] portalSpots, Set compensatedSpots, int width, int height) { Direction widthAxis = widthAxis(facing, upAxis); BlockPos supportOrigin = masterPos.relative(facing.getOpposite()); - Vec3 center = masterPos.getCenter() - .add(Vec3.atLowerCornerOf(upAxis.getNormal()).scale(0.5D)) + Vec3 center = Vec3.ZERO; + for (BlockPos portalSpot : portalSpots) { + center = center.add(portalSpot.getCenter()); + } + center = center.scale(1.0D / portalSpots.length) .subtract(Vec3.atLowerCornerOf(facing.getNormal()).scale(0.5D)); return new PortalGunPlacement( center, @@ -32,8 +38,10 @@ public record PortalGunPlacement( supportOrigin, masterPos, basePos, - new BlockPos[] {masterPos, basePos}, - compensatedSpots + portalSpots, + compensatedSpots, + width, + height ); } @@ -47,4 +55,47 @@ public record PortalGunPlacement( public static float yawFor(Direction facing, Direction upAxis) { return facing.getAxis() == Direction.Axis.Y ? upAxis.toYRot() : facing.toYRot(); } + + public static boolean isRectangularFootprint(BlockPos[] portalSpots, Direction facing, Direction upAxis, int width, int height) { + if (portalSpots == null || portalSpots.length != width * height || portalSpots.length == 0) { + return false; + } + Direction widthAxis = widthAxis(facing, upAxis); + BlockPos origin = portalSpots[0]; + Set cells = new HashSet<>(portalSpots.length); + int minWidth = Integer.MAX_VALUE; + int maxWidth = Integer.MIN_VALUE; + int minHeight = Integer.MAX_VALUE; + int maxHeight = Integer.MIN_VALUE; + for (BlockPos spot : portalSpots) { + if (spot == null) { + return false; + } + int dx = spot.getX() - origin.getX(); + int dy = spot.getY() - origin.getY(); + int dz = spot.getZ() - origin.getZ(); + int depth = dx * facing.getStepX() + dy * facing.getStepY() + dz * facing.getStepZ(); + int widthCell = dx * widthAxis.getStepX() + dy * widthAxis.getStepY() + dz * widthAxis.getStepZ(); + int heightCell = dx * upAxis.getStepX() + dy * upAxis.getStepY() + dz * upAxis.getStepZ(); + if (depth != 0 || !cells.add(((long) widthCell << 32) ^ (heightCell & 0xffffffffL))) { + return false; + } + minWidth = Math.min(minWidth, widthCell); + maxWidth = Math.max(maxWidth, widthCell); + minHeight = Math.min(minHeight, heightCell); + maxHeight = Math.max(maxHeight, heightCell); + } + if (maxWidth - minWidth != width - 1 || maxHeight - minHeight != height - 1 + || (minWidth != 0 && maxWidth != 0) || (minHeight != 0 && maxHeight != 0)) { + return false; + } + for (int widthCell = minWidth; widthCell <= maxWidth; widthCell++) { + for (int heightCell = minHeight; heightCell <= maxHeight; heightCell++) { + if (!cells.contains(((long) widthCell << 32) ^ (heightCell & 0xffffffffL))) { + return false; + } + } + } + return true; + } } diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/portalgun/PortalGunPortalEntity.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/portalgun/PortalGunPortalEntity.java index 0b163288..61986260 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/portalgun/PortalGunPortalEntity.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/portalgun/PortalGunPortalEntity.java @@ -2,16 +2,21 @@ package com.craisinlord.antarchy.content.portalgun; import com.craisinlord.antarchy.Antarchy; import com.craisinlord.antarchy.content.block.entity.PortalGunPortalBaseBlockEntity; +import com.craisinlord.antarchy.content.block.entity.PortalGunPortalCellAccess; +import com.craisinlord.antarchy.content.block.entity.PortalGunPortalFaceRecord; import com.craisinlord.antarchy.content.block.entity.PortalGunPortalMasterBlockEntity; import com.craisinlord.antarchy.mixins.AbstractArrowAccessor; +import com.craisinlord.antarchy.mixins.FallingBlockEntityAccessor; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.Random; import java.util.Set; import java.util.UUID; +import java.awt.Color; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.core.registries.BuiltInRegistries; @@ -19,14 +24,15 @@ import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.ListTag; import net.minecraft.nbt.Tag; import net.minecraft.network.protocol.game.ClientboundSetEntityMotionPacket; +import net.minecraft.network.protocol.game.ClientboundTeleportEntityPacket; import net.minecraft.network.syncher.EntityDataAccessor; import net.minecraft.network.syncher.EntityDataSerializers; import net.minecraft.network.syncher.SynchedEntityData; import net.minecraft.resources.ResourceLocation; +import net.minecraft.util.Mth; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.sounds.SoundEvent; -import net.minecraft.sounds.SoundEvents; import net.minecraft.sounds.SoundSource; import net.minecraft.world.damagesource.DamageSource; import net.minecraft.world.entity.Entity; @@ -34,6 +40,7 @@ import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.projectile.AbstractArrow; +import net.minecraft.world.entity.item.FallingBlockEntity; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.entity.BlockEntity; @@ -55,32 +62,35 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity { } private static final EntityDataAccessor SIDE = SynchedEntityData.defineId(PortalGunPortalEntity.class, EntityDataSerializers.INT); + private static final EntityDataAccessor> OWNER_ID = SynchedEntityData.defineId(PortalGunPortalEntity.class, EntityDataSerializers.OPTIONAL_UUID); + private static final EntityDataAccessor> GUN_ID = SynchedEntityData.defineId(PortalGunPortalEntity.class, EntityDataSerializers.OPTIONAL_UUID); + private static final EntityDataAccessor CHANNEL_NAME = SynchedEntityData.defineId(PortalGunPortalEntity.class, EntityDataSerializers.STRING); private static final EntityDataAccessor FACING = SynchedEntityData.defineId(PortalGunPortalEntity.class, EntityDataSerializers.INT); private static final EntityDataAccessor UP_AXIS = SynchedEntityData.defineId(PortalGunPortalEntity.class, EntityDataSerializers.INT); - private static final EntityDataAccessor CLOSING = SynchedEntityData.defineId(PortalGunPortalEntity.class, EntityDataSerializers.BOOLEAN); private static final EntityDataAccessor> LINKED_PORTAL = SynchedEntityData.defineId(PortalGunPortalEntity.class, EntityDataSerializers.OPTIONAL_UUID); + private static final EntityDataAccessor LINKED_PORTAL_ENTITY_ID = SynchedEntityData.defineId(PortalGunPortalEntity.class, EntityDataSerializers.INT); private static final EntityDataAccessor CHANNEL_RED = SynchedEntityData.defineId(PortalGunPortalEntity.class, EntityDataSerializers.INT); private static final EntityDataAccessor CHANNEL_GREEN = SynchedEntityData.defineId(PortalGunPortalEntity.class, EntityDataSerializers.INT); private static final EntityDataAccessor CHANNEL_BLUE = SynchedEntityData.defineId(PortalGunPortalEntity.class, EntityDataSerializers.INT); + private static final EntityDataAccessor PORTAL_WIDTH = SynchedEntityData.defineId(PortalGunPortalEntity.class, EntityDataSerializers.INT); + private static final EntityDataAccessor PORTAL_HEIGHT = SynchedEntityData.defineId(PortalGunPortalEntity.class, EntityDataSerializers.INT); + private static final EntityDataAccessor OPENING_ANIMATION_ID = SynchedEntityData.defineId(PortalGunPortalEntity.class, EntityDataSerializers.INT); private static final RawAnimation OPEN_ANIM = RawAnimation.begin().thenPlay("open"); private static final RawAnimation IDLE_ANIM = RawAnimation.begin().thenLoop("idle"); - private static final RawAnimation CLOSE_ANIM = RawAnimation.begin().thenPlay("close"); - private static final int OPEN_TICKS = 10; - private static final int CLOSE_TICKS = 10; - private static final int MAX_LIFETIME_TICKS = 20 * 60 * 20; - private static final int TELEPORT_COOLDOWN_TICKS = 40; - private static final double PLAYER_PORTAL_INSIDE_SHIFT = 0.05D; - private static final double HALF_WIDTH = 0.5D; - private static final double HALF_HEIGHT = 1.0D; + private static final int OPEN_TICKS = 5; + public static final int TELEPORT_COOLDOWN_TICKS = 3; + private static final double PORTAL_INSIDE_SHIFT = 0.05D; + private static final Set LOGGED_CROSSING_PATHS = new HashSet<>(); private static final double HALF_DEPTH = 0.35D; - private static final double MIN_EXIT_MOMENTUM = 0.42D; private final AnimatableInstanceCache geoCache = GeckoLibUtil.createInstanceCache(this); private final Map teleportCooldowns = new HashMap<>(); - private final Map portalEntityDepths = new HashMap<>(); - private Set lastScanEntities = new HashSet<>(); private UUID ownerId; + private String ownerIdentity; + private UUID gunId; + private String channelName; private UUID linkedPortalId; private int ageTicks; + private int lastOpeningAnimationId; private int pairTime; private BlockPos supportOrigin = BlockPos.ZERO; private BlockPos masterPos = BlockPos.ZERO; @@ -98,40 +108,56 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity { @Override protected void defineSynchedData(SynchedEntityData.Builder builder) { builder.define(SIDE, PortalSide.BLUE.ordinal()); + builder.define(OWNER_ID, Optional.empty()); + builder.define(GUN_ID, Optional.empty()); + builder.define(CHANNEL_NAME, ""); builder.define(FACING, Direction.NORTH.get3DDataValue()); builder.define(UP_AXIS, Direction.UP.get3DDataValue()); - builder.define(CLOSING, false); builder.define(LINKED_PORTAL, Optional.empty()); + builder.define(LINKED_PORTAL_ENTITY_ID, -1); builder.define(CHANNEL_RED, 58); builder.define(CHANNEL_GREEN, 166); builder.define(CHANNEL_BLUE, 255); + builder.define(PORTAL_WIDTH, 1); + builder.define(PORTAL_HEIGHT, 2); + builder.define(OPENING_ANIMATION_ID, 0); } - public void configure(UUID ownerId, PortalSide side, PortalGunPlacement placement) { + public void configure(UUID ownerId, UUID gunId, String ownerIdentity, String channelName, PortalSide side, PortalGunPlacement placement) { this.ownerId = ownerId; + this.ownerIdentity = ownerIdentity; + this.gunId = gunId; + this.channelName = channelName; + this.entityData.set(OWNER_ID, Optional.ofNullable(ownerId)); + this.entityData.set(GUN_ID, Optional.ofNullable(gunId)); + this.entityData.set(CHANNEL_NAME, channelName == null ? "" : channelName); this.entityData.set(SIDE, side.ordinal()); this.entityData.set(FACING, placement.facing().get3DDataValue()); this.entityData.set(UP_AXIS, placement.upAxis().get3DDataValue()); - int[] channelColor = generateChannelColor(ownerId, side); + this.entityData.set(PORTAL_WIDTH, placement.width()); + this.entityData.set(PORTAL_HEIGHT, placement.height()); + int[] channelColor = generateChannelColor(ownerIdentity, channelName, side); this.entityData.set(CHANNEL_RED, channelColor[0]); this.entityData.set(CHANNEL_GREEN, channelColor[1]); this.entityData.set(CHANNEL_BLUE, channelColor[2]); this.supportOrigin = placement.supportOrigin().immutable(); this.masterPos = placement.masterPos().immutable(); this.basePos = placement.basePos().immutable(); - this.portalSpots = new BlockPos[] {placement.portalSpots()[0].immutable(), placement.portalSpots()[1].immutable()}; + this.portalSpots = java.util.Arrays.stream(placement.portalSpots()).map(BlockPos::immutable).toArray(BlockPos[]::new); this.compensatedSpots = Set.copyOf(placement.compensatedSpots()); } public void linkTo(PortalGunPortalEntity other) { this.linkedPortalId = other.getUUID(); this.entityData.set(LINKED_PORTAL, Optional.of(other.getUUID())); + this.entityData.set(LINKED_PORTAL_ENTITY_ID, other.getId()); this.pairTime = (int) this.level().getGameTime(); } public void restorePair(UUID linkedPortalId, int pairTime) { this.linkedPortalId = linkedPortalId; this.entityData.set(LINKED_PORTAL, Optional.ofNullable(linkedPortalId)); + this.entityData.set(LINKED_PORTAL_ENTITY_ID, -1); this.pairTime = pairTime; } @@ -157,7 +183,45 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity { } public UUID getOwnerId() { - return this.ownerId; + return this.entityData.get(OWNER_ID).orElse(this.ownerId); + } + + public UUID getGunId() { + return this.entityData.get(GUN_ID).orElse(this.gunId); + } + + public String getChannelName() { + String synchronizedName = this.entityData.get(CHANNEL_NAME); + return synchronizedName.isEmpty() ? this.channelName : synchronizedName; + } + + public void adoptChannelIdentity(UUID gunId, String channelName) { + if (this.gunId == null && gunId != null) { + this.gunId = gunId; + this.entityData.set(GUN_ID, Optional.of(gunId)); + } + if (channelName != null && !channelName.isEmpty() && !channelName.equals(this.channelName)) { + this.channelName = channelName; + this.entityData.set(CHANNEL_NAME, channelName); + } + if (this.level() instanceof ServerLevel serverLevel && serverLevel.hasChunkAt(this.masterPos) + && serverLevel.getBlockEntity(this.masterPos) instanceof PortalGunPortalMasterBlockEntity master + && this.getUUID().equals(master.getPortalId())) { + master.adoptChannelIdentity(gunId, channelName); + } + } + + public void adoptGunId(UUID gunId) { + if (this.gunId != null || gunId == null) { + return; + } + this.gunId = gunId; + this.entityData.set(GUN_ID, Optional.of(gunId)); + if (this.level() instanceof ServerLevel serverLevel && serverLevel.hasChunkAt(this.masterPos) + && serverLevel.getBlockEntity(this.masterPos) instanceof com.craisinlord.antarchy.content.block.entity.PortalGunPortalMasterBlockEntity master + && this.getUUID().equals(master.getPortalId())) { + master.adoptGunId(gunId); + } } public int getChannelRed() { @@ -177,7 +241,27 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity { } public BlockPos[] getPortalSpots() { - return new BlockPos[] {this.portalSpots[0], this.portalSpots[1]}; + return this.portalSpots.clone(); + } + + public boolean containsPortalSpot(BlockPos pos) { + for (BlockPos portalSpot : this.portalSpots) { + if (portalSpot.equals(pos)) { + return true; + } + } + return false; + } + + public int getPortalWidth() { return this.entityData.get(PORTAL_WIDTH); } + + public int getPortalHeight() { return this.entityData.get(PORTAL_HEIGHT); } + + public void restartOpeningAnimation() { + int animationId = this.entityData.get(OPENING_ANIMATION_ID) + 1; + this.entityData.set(OPENING_ANIMATION_ID, animationId); + this.lastOpeningAnimationId = animationId; + this.ageTicks = 0; } public Set getCompensatedSpots() { @@ -193,12 +277,17 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity { if (linkedId == null) { return null; } - List entities = this.level().getEntities(this, new AABB(-3.0E7D, -3.0E7D, -3.0E7D, 3.0E7D, 3.0E7D, 3.0E7D), entity -> linkedId.equals(entity.getUUID())); - if (entities.isEmpty()) { - return null; + Entity linkedEntity; + if (this.level() instanceof ServerLevel serverLevel) { + linkedEntity = serverLevel.getEntity(linkedId); + } else { + int linkedEntityId = this.entityData.get(LINKED_PORTAL_ENTITY_ID); + if (linkedEntityId < 0) { + return null; + } + linkedEntity = this.level().getEntity(linkedEntityId); } - Entity entity = entities.getFirst(); - return entity instanceof PortalGunPortalEntity portal ? portal : null; + return linkedEntity instanceof PortalGunPortalEntity portal && !portal.isRemoved() ? portal : null; } public Vec3 getNormalVec() { @@ -214,7 +303,7 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity { } public PortalGunWorldPortalShape getWorldPortalShape() { - return new PortalGunWorldPortalShape(this.position(), this.getNormalVec().normalize(), this.getUpVec().normalize(), this.getWidthVec().normalize(), HALF_WIDTH, HALF_HEIGHT, HALF_DEPTH); + return new PortalGunWorldPortalShape(this.position(), this.getNormalVec().normalize(), this.getUpVec().normalize(), this.getWidthVec().normalize(), this.getPortalWidth() / 2.0D, this.getPortalHeight() / 2.0D, HALF_DEPTH); } public AABB getPlane() { @@ -234,10 +323,7 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity { } public AABB getPortalInsides(Entity entity) { - if (!(entity instanceof Player)) { - return this.getPortalInsides(); - } - double shiftAmount = Math.min(PLAYER_PORTAL_INSIDE_SHIFT, Math.abs(this.getWorldPortalShape().localCoords(entity.position()).depth())); + double shiftAmount = Math.min(PORTAL_INSIDE_SHIFT, Math.abs(this.getWorldPortalShape().localCoords(entity.position()).depth())); return this.getPortalInsides().move(this.getNormalVec().normalize().scale(shiftAmount)); } @@ -252,43 +338,100 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity { } public Vec3 teleportProbePosition(Entity entity) { - if (entity instanceof Player player) { - return player.getEyePosition(); - } - if (entity instanceof LivingEntity living) { - return entity.position().add(0.0D, living.getBbHeight() * 0.5D, 0.0D); - } - return entity.position(); + return entity.position().add(0.0D, this.getTeleportProbeHeight(entity), 0.0D); } - public Vec3 previousTeleportProbePosition(Entity entity) { - double prevX = entity.xo; - double prevY = entity.yo; - double prevZ = entity.zo; - if (entity instanceof Player player) { - return new Vec3(prevX, prevY + player.getEyeHeight(player.getPose()), prevZ); + public Vec3 teleportProbePosition(Entity entity, AABB bounds) { + double x = (bounds.minX + bounds.maxX) * 0.5D; + double y = bounds.minY; + double z = (bounds.minZ + bounds.maxZ) * 0.5D; + y += this.getTeleportProbeHeight(entity); + return new Vec3(x, y, z); + } + + private double getTeleportProbeHeight(Entity entity) { + return entity instanceof LivingEntity living ? living.getEyeHeight(living.getPose()) : entity.getEyeHeight(); + } + + public PortalCrossing resolveCrossing(Entity entity, AABB previousBox, AABB currentBox) { + Vec3 previousProbe = this.teleportProbePosition(entity, previousBox); + Vec3 currentProbe = this.teleportProbePosition(entity, currentBox); + Vec3 normal = this.getNormalVec().normalize(); + if (entity instanceof Player && Math.abs(normal.y) < 0.5D) { + double offset = Math.min(0.05D, Math.abs(this.position().subtract(entity.position()).dot(normal))); + Vec3 shift = normal.scale(offset); + if (this.getScanRange().move(shift).contains(currentProbe) + || !this.getPortalInsides().move(shift).contains(currentProbe)) { + return null; + } + PortalGunWorldPortalShape shape = this.getWorldPortalShape(); + AABB flatPlane = shape.getFlatPlane(); + boolean aligned; + if (Math.abs(normal.y) > 0.5D) { + aligned = currentBox.minX >= flatPlane.minX && currentBox.maxX <= flatPlane.maxX + && currentBox.minZ >= flatPlane.minZ && currentBox.maxZ <= flatPlane.maxZ; + } else { + aligned = currentBox.minY >= flatPlane.minY && currentBox.maxY <= flatPlane.maxY; + } + return aligned ? new PortalCrossing(currentProbe, offset) : null; } - if (entity instanceof LivingEntity living) { - return new Vec3(prevX, prevY + living.getBbHeight() * 0.5D, prevZ); - } - return new Vec3(prevX, prevY, prevZ); + return this.crossesPortal(previousProbe, currentProbe) ? new PortalCrossing(currentProbe, 0.0D) : null; } public Vec3 resolveCrossingProbe(Entity entity) { AABB currentBox = entity.getBoundingBox(); AABB previousBox = currentBox.move(entity.xo - entity.getX(), entity.yo - entity.getY(), entity.zo - entity.getZ()); - for (Vec3 currentSample : portalSamples(entity, currentBox, false)) { - Vec3 previousSample = matchPreviousSample(currentSample, currentBox, previousBox); - if (this.crossesPortal(previousSample, currentSample)) { - return currentSample; + PortalCrossing crossing = this.resolveCrossing(entity, previousBox, currentBox); + return crossing == null ? null : crossing.probe(); + } + + public PortalTransitTransform previewTransit(Entity entity, PortalGunPortalEntity destination, PortalCrossing crossing, Vec3 movement) { + Vec3 sourcePlaneCenter = this.position().add(this.getNormalVec().normalize().scale(crossing.normalOffset())); + Vec3 destinationPlaneCenter = destination.position().add(destination.getNormalVec().normalize().scale(crossing.normalOffset())); + Vec3 relativeProbe = crossing.probe().subtract(sourcePlaneCenter); + Vec3 transformedProbe = PortalGunTransformUtil.transformPosition(this, destination, relativeProbe); + Vec3 entityOffset = PortalGunTransformUtil.transformVector(this, destination, entity.position().subtract(crossing.probe())); + Vec3 requestedPosition = destinationPlaneCenter.add(transformedProbe).add(entityOffset); + Vec3 transformedMovement = PortalGunTransformUtil.transformVector(this, destination, movement); + Vec3 exitPosition = putEntityWithinExitBounds(entity, requestedPosition, legacyExitBounds(destination, entity, destination.getNormalVec().normalize())); + return new PortalTransitTransform(requestedPosition, exitPosition, transformedMovement); + } + + public static void teleportEntityAfterMovement(Entity entity, AABB previousBox, Vec3 resolvedMovement) { + if (entity.level().isClientSide || !entity.isAlive() || entity.isPassenger() || entity instanceof PortalGunPortalEntity) { + return; + } + AABB currentBox = entity.getBoundingBox(); + if (previousBox.getCenter().distanceToSqr(currentBox.getCenter()) <= 1.0E-10D) { + return; + } + AABB sweptBounds = previousBox.minmax(currentBox).inflate(1.0D); + for (PortalGunPortalEntity portal : findPortalsNearBounds(entity.level(), sweptBounds)) { + if (!(portal.level() instanceof ServerLevel serverLevel) || !portal.canTeleportEntity(entity)) { + continue; + } + PortalGunPortalEntity linked = portal.findLinkedPortal(serverLevel); + if (linked == null || !linked.isAlive()) { + continue; + } + PortalCrossing crossing = portal.resolveCrossing(entity, previousBox, currentBox); + if (crossing != null) { + logCrossingPath(entity, "movement"); + portal.teleportEntity(entity, linked, crossing.probe(), resolvedMovement, crossing.normalOffset()); + return; } } - Vec3 previousProbe = this.previousTeleportProbePosition(entity); - Vec3 currentProbe = this.teleportProbePosition(entity); - if (this.crossesPortal(previousProbe, currentProbe)) { - return currentProbe; + } + + public static List findPortalsNearBounds(Level level, AABB bounds) { + List portals = new java.util.ArrayList<>(); + AABB searchBounds = bounds.inflate(10.0D); + for (PortalGunPortalEntity portal : level.getEntitiesOfClass(PortalGunPortalEntity.class, searchBounds, PortalGunPortalEntity::isAlive)) { + if (portal.getWorldPortalShape().getBoundsForCulling().intersects(bounds)) { + portals.add(portal); + } } - return this.overlapsPortalVolume(entity) ? this.portalVolumeProbe(entity) : null; + return portals; } public boolean containsPoint(Vec3 position) { @@ -322,10 +465,20 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity { return this.getWorldPortalShape().shouldRenderFront(cameraPos); } + public float getPortalVisualScale(float partialTick) { + return Mth.clamp((this.ageTicks - 1.0F + partialTick) / OPEN_TICKS, 0.0F, 1.0F); + } + @Override public void tick() { super.tick(); - this.ageTicks++; + int openingAnimationId = this.entityData.get(OPENING_ANIMATION_ID); + if (openingAnimationId != this.lastOpeningAnimationId) { + this.lastOpeningAnimationId = openingAnimationId; + this.ageTicks = 0; + } else { + this.ageTicks++; + } this.yRotO = this.getYRot(); this.xRotO = this.getXRot(); if (this.level().isClientSide) { @@ -334,6 +487,14 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity { if (!(this.level() instanceof ServerLevel serverLevel)) { return; } + if (this.ownerId != null) { + PortalGunSavedData.getChannelNameForPortal(serverLevel.getServer(), this.ownerId, serverLevel.dimension().location(), this.getUUID()) + .ifPresent(channelName -> this.adoptChannelIdentity(this.gunId, channelName)); + if (this.gunId == null) { + PortalGunSavedData.getGunIdForPortal(serverLevel.getServer(), this.ownerId, serverLevel.dimension().location(), this.getUUID()) + .ifPresent(gunId -> this.adoptChannelIdentity(gunId, this.channelName)); + } + } this.tickTeleportCooldowns(); if (this.ownerId == null || !this.isStillRegistered(serverLevel)) { this.discard(); @@ -343,32 +504,28 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity { this.discard(); return; } - if (this.ageTicks >= MAX_LIFETIME_TICKS - CLOSE_TICKS) { - this.entityData.set(CLOSING, true); - } - if (this.ageTicks >= MAX_LIFETIME_TICKS) { - this.discard(); - return; - } PortalGunPortalEntity linked = this.findLinkedPortal(serverLevel); if (linked == null || !linked.isAlive()) { if (this.ageTicks % 100 == 0) { - Antarchy.LOGGER.info("Portal gun portal has no loaded linked portal portal={} owner={} side={} linked={}", this.getUUID(), this.ownerId, this.getPortalSide(), this.linkedPortalId); + Antarchy.LOGGER.debug("Portal gun portal has no loaded linked portal portal={} owner={} side={} linked={}", this.getUUID(), this.ownerId, this.getPortalSide(), this.linkedPortalId); } return; } if (this.ageTicks % 80 == 0) { - this.level().playSound(null, this.blockPosition(), sound("portal_ambient", SoundEvents.PORTAL_AMBIENT), SoundSource.BLOCKS, 0.18F, this.getPortalSide() == PortalSide.BLUE ? 1.05F : 0.92F); + this.level().playSound(null, this.blockPosition(), sound("portal_ambient"), SoundSource.BLOCKS, 0.18F, this.getPortalSide() == PortalSide.BLUE ? 1.05F : 0.92F); } this.tickTeleport(linked); } private boolean isStillRegistered(ServerLevel level) { - return this.ownerId != null && PortalGunSavedData.isRegistered(level.getServer(), this.ownerId, this.getPortalSide(), this.getUUID()); + return this.ownerId != null && PortalGunSavedData.isRegistered(level, this.ownerId, this.gunId, this.channelName, this.getPortalSide(), this.getUUID()); } private boolean hasValidSurface() { Direction facing = this.getFacingDirection(); + if (!PortalGunPlacement.isRectangularFootprint(this.portalSpots, this.getFacingDirection(), this.getUpAxis(), this.getPortalWidth(), this.getPortalHeight())) { + return false; + } for (int i = 0; i < this.portalSpots.length; i++) { BlockPos portalSpot = this.portalSpots[i]; BlockPos supportPos = portalSpot.relative(facing.getOpposite()); @@ -377,263 +534,173 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity { return false; } BlockEntity blockEntity = this.level().getBlockEntity(portalSpot); - if (i == 0) { - if (!(blockEntity instanceof PortalGunPortalMasterBlockEntity master) || !master.matches(this.ownerId, this.getUUID(), this.getPortalSide())) { - return false; - } - } else { - if (!(blockEntity instanceof PortalGunPortalBaseBlockEntity base) || !base.matches(this.ownerId, this.getUUID(), this.getPortalSide())) { - return false; - } - } + if (!(blockEntity instanceof PortalGunPortalCellAccess cell)) { + return false; + } + PortalGunPortalFaceRecord record = cell.portalGun$getFaceRecord(facing, this.getUUID()); + if (record != null) { + if (!this.ownerId.equals(record.ownerId()) || record.side() != this.getPortalSide() || record.master() != (i == 0)) { + return false; + } + } else if (i == 0) { + if (!(blockEntity instanceof PortalGunPortalMasterBlockEntity master) || !master.matches(this.ownerId, this.getUUID(), this.getPortalSide())) { + return false; + } + } else if (!(blockEntity instanceof PortalGunPortalBaseBlockEntity base) || !base.matches(this.ownerId, this.getUUID(), this.getPortalSide())) { + return false; + } } return true; } private void tickTeleport(PortalGunPortalEntity linked) { List entities = this.level().getEntities(this, this.getScanRange(), this::canTeleportEntity); - this.updatePortalEntityScan(linked, entities); - if (!entities.isEmpty() && this.ageTicks % 20 == 0) { - Antarchy.LOGGER.info("Portal gun teleport scan portal={} side={} linked={} count={} scan={}", this.getUUID(), this.getPortalSide(), linked.getUUID(), entities.size(), this.getScanRange()); - } for (Entity entity : entities) { - Vec3 crossingProbe = this.resolveCrossingProbe(entity); - if (crossingProbe == null) { - if (this.ageTicks % 40 == 0) { - PortalGunWorldPortalShape.PortalLocalCoords coords = this.getWorldPortalShape().localCoords(this.teleportProbePosition(entity)); - PortalBounds bounds = this.portalBounds(entity.getBoundingBox()); - Antarchy.LOGGER.info("Portal gun entity in scan did not cross portal={} entity={} entityType={} depth={} horizontal={} vertical={} boxDepthMin={} boxDepthMax={} boxHorizontalMin={} boxHorizontalMax={} boxVerticalMin={} boxVerticalMax={} movement={}", this.getUUID(), entity.getUUID(), EntityType.getKey(entity.getType()), coords.depth(), coords.horizontal(), coords.vertical(), bounds.minDepth(), bounds.maxDepth(), bounds.minHorizontal(), bounds.maxHorizontal(), bounds.minVertical(), bounds.maxVertical(), entity.getDeltaMovement()); - } + AABB currentBox = entity.getBoundingBox(); + AABB previousBox = currentBox.move(entity.xo - entity.getX(), entity.yo - entity.getY(), entity.zo - entity.getZ()); + PortalCrossing crossing = this.resolveCrossing(entity, previousBox, currentBox); + if (crossing == null) { continue; } - Antarchy.LOGGER.info("Portal gun teleport crossing portal={} side={} linked={} entity={} entityType={} probe={}", this.getUUID(), this.getPortalSide(), linked.getUUID(), entity.getUUID(), EntityType.getKey(entity.getType()), crossingProbe); - this.teleportEntity(entity, linked, crossingProbe); + logCrossingPath(entity, "portal_tick"); + Vec3 resolvedMovement = entity.position().subtract(entity.xo, entity.yo, entity.zo); + this.teleportEntity(entity, linked, crossing.probe(), resolvedMovement, crossing.normalOffset()); } } - private void updatePortalEntityScan(PortalGunPortalEntity linked, List entities) { - Set currentScan = new HashSet<>(); - for (Entity entity : entities) { - UUID entityId = entity.getUUID(); - currentScan.add(entityId); - PortalBounds bounds = this.portalBounds(entity.getBoundingBox()); - int depthBucket = (int) Math.round(bounds.minDepth() * 1000.0D); - this.portalEntityDepths.put(entityId, depthBucket); - if (!this.lastScanEntities.contains(entityId)) { - Antarchy.LOGGER.info("Portal gun entity entered scan portal={} linked={} entity={} entityType={} minDepth={} maxDepth={} minHorizontal={} maxHorizontal={} minVertical={} maxVertical={} motion={}", this.getUUID(), linked.getUUID(), entityId, EntityType.getKey(entity.getType()), bounds.minDepth(), bounds.maxDepth(), bounds.minHorizontal(), bounds.maxHorizontal(), bounds.minVertical(), bounds.maxVertical(), entity.getDeltaMovement()); - } + private static void logCrossingPath(Entity entity, String path) { + String entityType = BuiltInRegistries.ENTITY_TYPE.getKey(entity.getType()).toString(); + if (LOGGED_CROSSING_PATHS.add(entityType + ":" + path)) { + Antarchy.LOGGER.info("Portal gun entity crossing path entityType={} path={}", entityType, path); } - for (UUID previous : this.lastScanEntities) { - if (!currentScan.contains(previous)) { - Integer depth = this.portalEntityDepths.remove(previous); - Antarchy.LOGGER.info("Portal gun entity left scan portal={} linked={} entity={} lastDepth={}", this.getUUID(), linked.getUUID(), previous, depth); - } - } - this.lastScanEntities = currentScan; } private boolean canTeleportEntity(Entity entity) { - if (!entity.isAlive() || entity instanceof PortalGunPortalEntity || entity.isPassenger() || entity.isVehicle()) { + if (!entity.isAlive() || entity instanceof PortalGunPortalEntity || entity.isPassenger()) { return false; } - return !this.teleportCooldowns.containsKey(entity.getUUID()); + return !this.isTeleportCoolingDown(entity); } - private boolean isInsidePortal(Vec3 position) { - return this.getWorldPortalShape().contains(position, 0.0D); + public boolean isTeleportCoolingDown(Entity entity) { + return this.teleportCooldowns.containsKey(entity.getUUID()) || entity.getPortalCooldown() > 0; } - private PortalLocalCoords localCoords(Vec3 position) { - PortalGunWorldPortalShape.PortalLocalCoords coords = this.getWorldPortalShape().localCoords(position); - return new PortalLocalCoords(coords.horizontal(), coords.vertical(), coords.depth()); + private static Vec3 clampPortalMotion(Vec3 motion) { + return new Vec3(clampPortalMotionComponent(motion.x), clampPortalMotionComponent(motion.y), clampPortalMotionComponent(motion.z)); } - private boolean overlapsPortalVolume(Entity entity) { - PortalBounds bounds = this.portalBounds(entity.getBoundingBox()); - double edgePadding = entity instanceof Player ? 0.22D : 0.14D; - double frontDepth = entity instanceof Player ? 0.5D : 0.36D; - double backDepth = entity instanceof Player ? 0.28D : 0.22D; - double normalVelocity = entity.getDeltaMovement().dot(this.getNormalVec().normalize()); - boolean movingIntoPortal = normalVelocity < -0.01D; - boolean straddlingPlane = bounds.minDepth() <= 0.03D && bounds.maxDepth() >= -0.03D; - boolean inFrame = bounds.maxHorizontal() >= -HALF_WIDTH - edgePadding - && bounds.minHorizontal() <= HALF_WIDTH + edgePadding - && bounds.maxVertical() >= -HALF_HEIGHT - edgePadding - && bounds.minVertical() <= HALF_HEIGHT + edgePadding - && bounds.maxDepth() >= -backDepth - && bounds.minDepth() <= frontDepth; - return inFrame && (movingIntoPortal || straddlingPlane); + private static double clampPortalMotionComponent(double motion) { + return Math.abs(motion) > 0.99D ? motion / (Math.abs(motion) + 0.001D) : motion; } - private Vec3 portalVolumeProbe(Entity entity) { - Vec3 probe = this.teleportProbePosition(entity); - PortalGunWorldPortalShape shape = this.getWorldPortalShape(); - PortalGunWorldPortalShape.PortalLocalCoords coords = shape.localCoords(probe); - double horizontal = clamp(coords.horizontal(), -HALF_WIDTH + 0.05D, HALF_WIDTH - 0.05D); - double vertical = clamp(coords.vertical(), -HALF_HEIGHT + 0.05D, HALF_HEIGHT - 0.05D); - return shape.center() - .add(shape.right().scale(horizontal)) - .add(shape.up().scale(vertical)) - .subtract(shape.normal().scale(0.01D)); - } - - private PortalBounds portalBounds(AABB bounds) { - PortalGunWorldPortalShape shape = this.getWorldPortalShape(); - double minHorizontal = Double.POSITIVE_INFINITY; - double maxHorizontal = Double.NEGATIVE_INFINITY; - double minVertical = Double.POSITIVE_INFINITY; - double maxVertical = Double.NEGATIVE_INFINITY; - double minDepth = Double.POSITIVE_INFINITY; - double maxDepth = Double.NEGATIVE_INFINITY; - for (Vec3 corner : boxCorners(bounds)) { - PortalGunWorldPortalShape.PortalLocalCoords coords = shape.localCoords(corner); - minHorizontal = Math.min(minHorizontal, coords.horizontal()); - maxHorizontal = Math.max(maxHorizontal, coords.horizontal()); - minVertical = Math.min(minVertical, coords.vertical()); - maxVertical = Math.max(maxVertical, coords.vertical()); - minDepth = Math.min(minDepth, coords.depth()); - maxDepth = Math.max(maxDepth, coords.depth()); + private void teleportEntity(Entity entity, PortalGunPortalEntity destination, Vec3 currentProbe, Vec3 entryMotion, double normalOffset) { + List group = new java.util.ArrayList<>(); + collectTeleportGroup(entity, null, group); + Vec3 rootOriginalPosition = entity.position(); + PortalTransitTransform transitTransform = this.previewTransit(entity, destination, new PortalCrossing(currentProbe, normalOffset), entryMotion); + Vec3 exitNormal = destination.getNormalVec().normalize(); + Vec3 requestedExitPos = transitTransform.requestedPosition(); + Vec3 exitPos = transitTransform.exitPosition(); + Vec3 transformedMotion = transitTransform.movement(); + this.level().playSound(null, this.getX(), this.getY(), this.getZ(), sound("portal_enter"), SoundSource.PLAYERS, 0.7F, 0.96F + this.random.nextFloat() * 0.08F); + for (EntityTeleportState state : group) { + Entity member = state.entity(); + member.stopRiding(); } - return new PortalBounds(minHorizontal, maxHorizontal, minVertical, maxVertical, minDepth, maxDepth); - } - - private static Vec3[] boxCorners(AABB bounds) { - return new Vec3[] { - new Vec3(bounds.minX, bounds.minY, bounds.minZ), - new Vec3(bounds.minX, bounds.minY, bounds.maxZ), - new Vec3(bounds.minX, bounds.maxY, bounds.minZ), - new Vec3(bounds.minX, bounds.maxY, bounds.maxZ), - new Vec3(bounds.maxX, bounds.minY, bounds.minZ), - new Vec3(bounds.maxX, bounds.minY, bounds.maxZ), - new Vec3(bounds.maxX, bounds.maxY, bounds.minZ), - new Vec3(bounds.maxX, bounds.maxY, bounds.maxZ) - }; - } - - private static double clamp(double value, double min, double max) { - return Math.max(min, Math.min(max, value)); - } - - private List portalSamples(Entity entity, AABB box, boolean previous) { - Vec3 center = new Vec3((box.minX + box.maxX) * 0.5D, (box.minY + box.maxY) * 0.5D, (box.minZ + box.maxZ) * 0.5D); - Vec3 topCenter = new Vec3(center.x, box.maxY, center.z); - Vec3 bottomCenter = new Vec3(center.x, box.minY, center.z); - if (entity instanceof Player player) { - Vec3 eye = previous ? this.previousTeleportProbePosition(player) : this.teleportProbePosition(player); - return List.of(center, eye, topCenter, bottomCenter); + for (EntityTeleportState state : group) { + Entity member = state.entity(); + Vec3 memberOffset = PortalGunTransformUtil.transformVector(this, destination, state.position().subtract(rootOriginalPosition)); + Vec3 memberExitPos = putEntityWithinExitBounds(member, requestedExitPos.add(memberOffset), legacyExitBounds(destination, member, exitNormal)); + Vec3 memberLook = PortalGunTransformUtil.transformVector(this, destination, state.look()).normalize(); + Vec3 memberMotion = member == entity + ? transformedMotion + : PortalGunTransformUtil.transformVector(this, destination, state.motion()); + memberMotion = clampPortalMotion(memberMotion); + float yaw = PortalGunTransformUtil.yawFromLook(memberLook); + float pitch = PortalGunTransformUtil.pitchFromLook(memberLook); + if (member instanceof ServerPlayer player && destination.level() instanceof ServerLevel destinationLevel) { + player.teleportTo(destinationLevel, memberExitPos.x, memberExitPos.y, memberExitPos.z, yaw, pitch); + } else { + member.teleportTo(memberExitPos.x, memberExitPos.y, memberExitPos.z); + member.setYRot(yaw); + member.setXRot(pitch); + member.setYHeadRot(yaw); + member.setYBodyRot(yaw); + } + member.setDeltaMovement(memberMotion); + member.hasImpulse = true; + member.hurtMarked = true; + if (member instanceof ServerPlayer player) { + player.connection.send(new ClientboundSetEntityMotionPacket(player)); + } + member.xo = member.getX(); + member.yo = member.getY(); + member.zo = member.getZ(); + member.yRotO = yaw; + member.xRotO = pitch; + member.setOnGround(false); + member.horizontalCollision = false; + member.verticalCollision = false; + member.verticalCollisionBelow = false; + float verticalMotion = (float) memberMotion.y; + member.fallDistance = 0.1F * (verticalMotion / -0.1F * verticalMotion / -0.1F); + this.applyTeleportCooldown(member, destination); + this.handleSpecialEntityPostTeleport(member); } - if (entity instanceof LivingEntity) { - return List.of(center, topCenter, bottomCenter); - } - return List.of(center); - } - - private Vec3 matchPreviousSample(Vec3 currentSample, AABB currentBox, AABB previousBox) { - double xLerp = axisRatio(currentSample.x, currentBox.minX, currentBox.maxX); - double yLerp = axisRatio(currentSample.y, currentBox.minY, currentBox.maxY); - double zLerp = axisRatio(currentSample.z, currentBox.minZ, currentBox.maxZ); - return new Vec3( - lerp(previousBox.minX, previousBox.maxX, xLerp), - lerp(previousBox.minY, previousBox.maxY, yLerp), - lerp(previousBox.minZ, previousBox.maxZ, zLerp) - ); - } - - private static double axisRatio(double value, double min, double max) { - double size = max - min; - if (Math.abs(size) < 1.0E-6D) { - return 0.5D; - } - return (value - min) / size; - } - - private static double lerp(double min, double max, double delta) { - return min + (max - min) * delta; - } - - private void teleportEntity(Entity entity, PortalGunPortalEntity destination, Vec3 currentProbe) { - Vec3 relativeProbe = currentProbe.subtract(this.position()); - Vec3 transformedProbe = PortalGunTransformUtil.transformPosition(this, destination, relativeProbe); - Vec3 entityOffsetFromProbe = entity.position().subtract(currentProbe); - Vec3 transformedEntityOffset = PortalGunTransformUtil.transformVector(this, destination, entityOffsetFromProbe); - Vec3 transformedLook = PortalGunTransformUtil.transformVector(this, destination, entity.getLookAngle()).normalize(); - Vec3 entryMotion = this.portalEntryMomentum(entity, currentProbe); - Vec3 transformedMotion = this.exitMomentum(entity, destination, PortalGunTransformUtil.transformVector(this, destination, entryMotion)); - double exitClearance = Math.max(0.35D, entity.getBbWidth() * 0.5D + 0.16D); - Vec3 exitProbe = destination.position().add(transformedProbe).add(destination.getNormalVec().normalize().scale(exitClearance - transformedProbe.dot(destination.getNormalVec().normalize()))); - Vec3 requestedExitPos = exitProbe.add(transformedEntityOffset); - Vec3 exitPos = this.resolveSafeExitPosition(entity, destination, requestedExitPos); - float yaw = PortalGunTransformUtil.yawFromLook(transformedLook); - float pitch = PortalGunTransformUtil.pitchFromLook(transformedLook); - this.applyTeleportCooldown(entity, destination); - this.level().playSound(null, this.getX(), this.getY(), this.getZ(), sound("portal_enter", SoundEvents.PORTAL_TRAVEL), SoundSource.PLAYERS, 0.7F, 0.96F + this.random.nextFloat() * 0.08F); - if (entity instanceof ServerPlayer player && destination.level() instanceof ServerLevel destinationLevel) { - player.teleportTo(destinationLevel, exitPos.x, exitPos.y, exitPos.z, yaw, pitch); - } else { - entity.teleportTo(exitPos.x, exitPos.y, exitPos.z); - entity.setYRot(yaw); - entity.setXRot(pitch); - entity.setYHeadRot(yaw); - entity.setYBodyRot(yaw); - } - entity.setDeltaMovement(transformedMotion); - entity.hasImpulse = true; - entity.hurtMarked = true; - if (entity instanceof ServerPlayer player) { - player.connection.send(new ClientboundSetEntityMotionPacket(player)); - } - entity.fallDistance = 0.0F; - destination.level().playSound(null, exitPos.x, exitPos.y, exitPos.z, sound("portal_exit", SoundEvents.PORTAL_TRAVEL), SoundSource.PLAYERS, 0.7F, 0.96F + this.random.nextFloat() * 0.08F); - this.handleSpecialEntityPostTeleport(entity); - Antarchy.LOGGER.info("Portal gun teleported entity source={} destination={} entity={} entityType={} requestedExit={} exit={} inputVelocity={} outputVelocity={} yaw={} pitch={} sourceFacing={} sourceUp={} destinationFacing={} destinationUp={}", this.getUUID(), destination.getUUID(), entity.getUUID(), EntityType.getKey(entity.getType()), requestedExitPos, exitPos, entryMotion, transformedMotion, yaw, pitch, this.getFacingDirection(), this.getUpAxis(), destination.getFacingDirection(), destination.getUpAxis()); - } - - private Vec3 portalEntryMomentum(Entity entity, Vec3 currentProbe) { - Vec3 velocity = entity.getDeltaMovement(); - Vec3 probeVelocity = currentProbe.subtract(this.previousTeleportProbePosition(entity)); - Vec3 positionVelocity = entity.position().subtract(entity.xo, entity.yo, entity.zo); - Vec3 selected = velocity; - if (probeVelocity.lengthSqr() > selected.lengthSqr()) { - selected = probeVelocity; - } - if (positionVelocity.lengthSqr() > selected.lengthSqr()) { - selected = positionVelocity; - } - return selected; - } - - private Vec3 exitMomentum(Entity entity, PortalGunPortalEntity destination, Vec3 transformedMotion) { - Vec3 normal = destination.getNormalVec().normalize(); - double outward = transformedMotion.dot(normal); - if (outward >= MIN_EXIT_MOMENTUM) { - return transformedMotion; - } - if (entity.getDeltaMovement().lengthSqr() <= 1.0E-5D && transformedMotion.lengthSqr() <= 1.0E-5D) { - return normal.scale(MIN_EXIT_MOMENTUM); - } - return transformedMotion.add(normal.scale(MIN_EXIT_MOMENTUM - outward)); - } - - private Vec3 resolveSafeExitPosition(Entity entity, PortalGunPortalEntity destination, Vec3 requestedExitPos) { - Vec3 normal = destination.getNormalVec().normalize(); - if (this.canPlaceEntityAt(entity, requestedExitPos)) { - return requestedExitPos; - } - for (int i = 1; i <= 24; i++) { - Vec3 candidate = requestedExitPos.add(normal.scale(i * 0.125D)); - if (this.canPlaceEntityAt(entity, candidate)) { - Antarchy.LOGGER.info("Portal gun adjusted blocked exit entity={} destination={} requested={} adjusted={} distance={}", entity.getUUID(), destination.getUUID(), requestedExitPos, candidate, i * 0.125D); - return candidate; + for (EntityTeleportState state : group) { + if (state.vehicle() != null && state.entity().isAlive()) { + state.entity().startRiding(state.vehicle(), true); } } - Vec3 fallback = destination.position().add(normal.scale(Math.max(1.0D, entity.getBbWidth() + 0.5D))); - Antarchy.LOGGER.info("Portal gun could not find clear exit entity={} destination={} requested={} fallback={}", entity.getUUID(), destination.getUUID(), requestedExitPos, fallback); - return fallback; + if (destination.level() instanceof ServerLevel destinationLevel) { + for (EntityTeleportState state : group) { + Entity member = state.entity(); + if (!(member instanceof ServerPlayer)) { + destinationLevel.getChunkSource().broadcastAndSend(member, new ClientboundTeleportEntityPacket(member)); + destinationLevel.getChunkSource().broadcastAndSend(member, new ClientboundSetEntityMotionPacket(member)); + } + } + } + destination.level().playSound(null, exitPos.x, exitPos.y, exitPos.z, sound("portal_exit"), SoundSource.PLAYERS, 0.7F, 0.96F + this.random.nextFloat() * 0.08F); } - private boolean canPlaceEntityAt(Entity entity, Vec3 position) { - AABB movedBounds = entity.getBoundingBox().move(position.x - entity.getX(), position.y - entity.getY(), position.z - entity.getZ()); - return this.level().noCollision(entity, movedBounds); + private void collectTeleportGroup(Entity entity, Entity vehicle, List group) { + group.add(new EntityTeleportState(entity, vehicle, entity.position(), entity.getLookAngle(), entity.getDeltaMovement())); + for (Entity passenger : entity.getPassengers()) { + collectTeleportGroup(passenger, entity, group); + } + } + + private static Vec3 putEntityWithinExitBounds(Entity entity, Vec3 position, AABB exitBounds) { + AABB entityBounds = entity.getBoundingBox().move(position.x - entity.getX(), position.y - entity.getY(), position.z - entity.getZ()); + double x = position.x; + double y = position.y; + double z = position.z; + if (entityBounds.maxX > exitBounds.maxX) { + x += exitBounds.maxX - entityBounds.maxX; + } + if (entityBounds.minX < exitBounds.minX) { + x += exitBounds.minX - entityBounds.minX; + } + if (y + entity.getEyeHeight() > exitBounds.maxY) { + y += exitBounds.maxY - y - entity.getEyeHeight(); + } + if (y < exitBounds.minY) { + y += exitBounds.minY - y + 0.001D; + } + if (entityBounds.maxZ > exitBounds.maxZ) { + z += exitBounds.maxZ - entityBounds.maxZ; + } + if (entityBounds.minZ < exitBounds.minZ) { + z += exitBounds.minZ - entityBounds.minZ; + } + return new Vec3(x, y, z); + } + + private static AABB legacyExitBounds(PortalGunPortalEntity destination, Entity entity, Vec3 exitNormal) { + double maxDimension = Math.max(entity.getBbWidth(), entity.getBbHeight()); + return destination.getScanRange().expandTowards(exitNormal.scale(-maxDimension)); } private void tickTeleportCooldowns() { @@ -653,9 +720,13 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity { UUID entityId = entity.getUUID(); this.teleportCooldowns.put(entityId, TELEPORT_COOLDOWN_TICKS); destination.teleportCooldowns.put(entityId, TELEPORT_COOLDOWN_TICKS); + entity.setPortalCooldown(TELEPORT_COOLDOWN_TICKS); } private void handleSpecialEntityPostTeleport(Entity entity) { + if (entity instanceof FallingBlockEntity) { + ((FallingBlockEntityAccessor) entity).antarchy$setTime(2); + } if (entity instanceof AbstractArrow arrow) { ((AbstractArrowAccessor) arrow).antarchy$setInGround(false); ((AbstractArrowAccessor) arrow).antarchy$setInGroundTime(0); @@ -687,6 +758,15 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity { if (this.ownerId != null) { tag.putUUID("OwnerId", this.ownerId); } + if (this.ownerIdentity != null) { + tag.putString("OwnerIdentity", this.ownerIdentity); + } + if (this.gunId != null) { + tag.putUUID("GunId", this.gunId); + } + if (this.channelName != null) { + tag.putString("ChannelName", this.channelName); + } if (this.linkedPortalId != null) { tag.putUUID("LinkedPortalId", this.linkedPortalId); } @@ -698,6 +778,8 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity { tag.putInt("ChannelRed", this.getChannelRed()); tag.putInt("ChannelGreen", this.getChannelGreen()); tag.putInt("ChannelBlue", this.getChannelBlue()); + tag.putInt("PortalWidth", this.getPortalWidth()); + tag.putInt("PortalHeight", this.getPortalHeight()); tag.putInt("SupportX", this.supportOrigin.getX()); tag.putInt("SupportY", this.supportOrigin.getY()); tag.putInt("SupportZ", this.supportOrigin.getZ()); @@ -732,6 +814,12 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity { if (tag.hasUUID("OwnerId")) { this.ownerId = tag.getUUID("OwnerId"); } + this.ownerIdentity = tag.contains("OwnerIdentity") ? tag.getString("OwnerIdentity") : String.valueOf(this.ownerId); + this.gunId = tag.hasUUID("GunId") ? tag.getUUID("GunId") : null; + this.channelName = tag.contains("ChannelName") ? tag.getString("ChannelName") : this.gunId == null ? "" : "Random Channel #" + this.gunId.hashCode(); + this.entityData.set(OWNER_ID, Optional.ofNullable(this.ownerId)); + this.entityData.set(GUN_ID, Optional.ofNullable(this.gunId)); + this.entityData.set(CHANNEL_NAME, this.channelName); if (tag.hasUUID("LinkedPortalId")) { this.linkedPortalId = tag.getUUID("LinkedPortalId"); this.entityData.set(LINKED_PORTAL, Optional.of(this.linkedPortalId)); @@ -741,24 +829,29 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity { this.entityData.set(SIDE, tag.getInt("Side")); this.entityData.set(FACING, tag.getInt("Facing")); this.entityData.set(UP_AXIS, tag.getInt("UpAxis")); - if (tag.contains("ChannelRed")) { - this.entityData.set(CHANNEL_RED, tag.getInt("ChannelRed")); - this.entityData.set(CHANNEL_GREEN, tag.getInt("ChannelGreen")); - this.entityData.set(CHANNEL_BLUE, tag.getInt("ChannelBlue")); - } else if (this.ownerId != null) { - int[] channelColor = generateChannelColor(this.ownerId, this.getPortalSide()); + if (this.ownerId != null && ("Global".equals(this.ownerIdentity) || !tag.contains("ChannelRed"))) { + int[] channelColor = generateChannelColor(this.ownerIdentity, this.channelName, this.getPortalSide()); this.entityData.set(CHANNEL_RED, channelColor[0]); this.entityData.set(CHANNEL_GREEN, channelColor[1]); this.entityData.set(CHANNEL_BLUE, channelColor[2]); + } else if (tag.contains("ChannelRed")) { + this.entityData.set(CHANNEL_RED, tag.getInt("ChannelRed")); + this.entityData.set(CHANNEL_GREEN, tag.getInt("ChannelGreen")); + this.entityData.set(CHANNEL_BLUE, tag.getInt("ChannelBlue")); } this.supportOrigin = new BlockPos(tag.getInt("SupportX"), tag.getInt("SupportY"), tag.getInt("SupportZ")); this.masterPos = new BlockPos(tag.getInt("MasterX"), tag.getInt("MasterY"), tag.getInt("MasterZ")); this.basePos = new BlockPos(tag.getInt("BaseX"), tag.getInt("BaseY"), tag.getInt("BaseZ")); ListTag spots = tag.getList("PortalSpots", Tag.TAG_COMPOUND); - for (int i = 0; i < Math.min(2, spots.size()); i++) { + if (!spots.isEmpty()) { + this.portalSpots = new BlockPos[spots.size()]; + } + for (int i = 0; i < spots.size(); i++) { CompoundTag spot = spots.getCompound(i); this.portalSpots[i] = new BlockPos(spot.getInt("X"), spot.getInt("Y"), spot.getInt("Z")); } + this.entityData.set(PORTAL_WIDTH, Mth.clamp(tag.getInt("PortalWidth"), 1, 16)); + this.entityData.set(PORTAL_HEIGHT, Mth.clamp(tag.getInt("PortalHeight"), 2, 16)); ListTag compensated = tag.getList("CompensatedSpots", Tag.TAG_COMPOUND); Set compensatedSpots = new HashSet<>(); for (int i = 0; i < compensated.size(); i++) { @@ -772,21 +865,30 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity { public void remove(Entity.RemovalReason reason) { Level level = this.level(); UUID ownerId = this.ownerId; + boolean destroyPortal = reason.shouldDestroy(); + if (destroyPortal && !level.isClientSide && ownerId != null && level instanceof ServerLevel serverLevel) { + PortalGunSavedData.clearPortal(serverLevel.getServer(), ownerId, this.gunId, this.channelName, this.getPortalSide(), this.getUUID(), serverLevel.dimension().location()); + } super.remove(reason); - if (!level.isClientSide && ownerId != null && level instanceof ServerLevel serverLevel) { - PortalGunSavedData.clearPortal(serverLevel.getServer(), ownerId, this.getPortalSide(), this.getUUID()); + if (destroyPortal && !level.isClientSide && ownerId != null && level instanceof ServerLevel serverLevel) { this.clearPortalBlocks(serverLevel); } } private void clearPortalBlocks(ServerLevel level) { for (BlockPos portalSpot : this.portalSpots) { - if (level.getBlockEntity(portalSpot) instanceof PortalGunPortalMasterBlockEntity master && this.getUUID().equals(master.getPortalId())) { - level.removeBlock(portalSpot, false); - continue; - } - if (level.getBlockEntity(portalSpot) instanceof PortalGunPortalBaseBlockEntity base && this.getUUID().equals(base.getPortalId())) { - level.removeBlock(portalSpot, false); + if (level.getBlockEntity(portalSpot) instanceof PortalGunPortalCellAccess cell) { + PortalGunPortalFaceRecord removed = cell.portalGun$removeFaceRecord(this.getFacingDirection(), this.getUUID()); + if (!cell.portalGun$getFaceRecords().isEmpty()) { + continue; + } + if (cell instanceof PortalGunPortalMasterBlockEntity master && this.getUUID().equals(master.getPortalId())) { + level.removeBlock(portalSpot, false); + } else if (cell instanceof PortalGunPortalBaseBlockEntity base && this.getUUID().equals(base.getPortalId())) { + level.removeBlock(portalSpot, false); + } else if (removed != null) { + level.removeBlock(portalSpot, false); + } } } } @@ -826,10 +928,7 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity { } private PlayState portalController(AnimationState state) { - state.getController().setAnimationSpeed(this.entityData.get(CLOSING) || this.ageTicks < OPEN_TICKS ? 4.0D : 1.0D); - if (this.entityData.get(CLOSING)) { - return state.setAndContinue(CLOSE_ANIM); - } + state.getController().setAnimationSpeed(this.ageTicks < OPEN_TICKS ? 4.0D : 1.0D); if (this.ageTicks < OPEN_TICKS) { return state.setAndContinue(OPEN_ANIM); } @@ -841,24 +940,48 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity { return this.geoCache; } - public static SoundEvent sound(String path, SoundEvent fallback) { - return BuiltInRegistries.SOUND_EVENT.getOptional(ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, path)).orElse(fallback); + public static SoundEvent sound(String path) { + ResourceLocation soundId = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, path); + return BuiltInRegistries.SOUND_EVENT.getOptional(soundId) + .orElseThrow(() -> new IllegalStateException("Missing portal gun sound event: " + soundId)); } - private static int[] generateChannelColor(UUID ownerId, PortalSide side) { - int seed = ownerId == null ? side.ordinal() : ownerId.hashCode() ^ side.ordinal() * 0x45d9f3b; - int wobbleA = Math.floorMod(seed, 54); - int wobbleB = Math.floorMod(seed >> 8, 46); - int wobbleC = Math.floorMod(seed >> 16, 38); - if (side == PortalSide.BLUE) { - return new int[] {42 + wobbleC, 128 + wobbleB, 218 + wobbleA / 3}; + private static int[] generateChannelColor(String ownerIdentity, String channelName, PortalSide side) { + if ("Global".equals(ownerIdentity)) { + int[] globalColors = switch (channelName) { + case "Chell" -> new int[] {361215, 16756742}; + case "Atlas" -> new int[] {5482192, 4064209}; + case "P-body" -> new int[] {16373344, 8394260}; + default -> null; + }; + if (globalColors != null) { + int color = globalColors[side == PortalSide.BLUE ? 0 : 1]; + return new int[] {color >> 16 & 0xFF, color >> 8 & 0xFF, color & 0xFF}; + } } - return new int[] {230 + wobbleA / 5, 108 + wobbleB, 28 + wobbleC}; + String owner = String.valueOf(ownerIdentity); + String identity = owner + "_" + String.valueOf(channelName); + Random random = new Random(); + random.setSeed((long) Math.abs(identity.hashCode() * owner.hashCode())); + int firstColor = Math.round(1.6777215E7F * random.nextFloat()); + float[] hsb = Color.RGBtoHSB(firstColor >> 16 & 0xFF, firstColor >> 8 & 0xFF, firstColor & 0xFF, null); + hsb[2] = 0.65F + 0.25F * hsb[2]; + int portalAColor = Color.HSBtoRGB(hsb[0], hsb[1], hsb[2]); + float oppositeHue = hsb[0] + 0.5F; + if (oppositeHue > 1.0F) { + oppositeHue -= 1.0F; + } + int portalBColor = Color.HSBtoRGB(oppositeHue, hsb[1], hsb[2]); + int color = side == PortalSide.BLUE ? portalAColor : portalBColor; + return new int[] {color >> 16 & 0xFF, color >> 8 & 0xFF, color & 0xFF}; } - private record PortalLocalCoords(double horizontal, double vertical, double depth) { + public record PortalCrossing(Vec3 probe, double normalOffset) { } - private record PortalBounds(double minHorizontal, double maxHorizontal, double minVertical, double maxVertical, double minDepth, double maxDepth) { + public record PortalTransitTransform(Vec3 requestedPosition, Vec3 exitPosition, Vec3 movement) { + } + + private record EntityTeleportState(Entity entity, Entity vehicle, Vec3 position, Vec3 look, Vec3 motion) { } } diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/portalgun/PortalGunProjectileEntity.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/portalgun/PortalGunProjectileEntity.java index 42c030e3..cce00a1d 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/portalgun/PortalGunProjectileEntity.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/portalgun/PortalGunProjectileEntity.java @@ -1,40 +1,72 @@ package com.craisinlord.antarchy.content.portalgun; import com.craisinlord.antarchy.content.item.PortalGunItem; +import com.craisinlord.antarchy.config.AntarchySettings; +import java.util.Comparator; +import java.util.LinkedHashMap; +import java.util.Map; import java.util.UUID; import net.minecraft.core.BlockPos; import net.minecraft.core.component.DataComponents; +import net.minecraft.core.registries.Registries; import net.minecraft.nbt.CompoundTag; import net.minecraft.network.syncher.EntityDataAccessor; import net.minecraft.network.syncher.EntityDataSerializers; import net.minecraft.network.syncher.SynchedEntityData; +import net.minecraft.resources.ResourceLocation; import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.TicketType; +import net.minecraft.tags.TagKey; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.EntityDimensions; import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.Pose; import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.projectile.ThrowableItemProjectile; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; +import net.minecraft.world.level.ClipContext; +import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.StainedGlassBlock; +import net.minecraft.world.level.block.StainedGlassPaneBlock; +import net.minecraft.world.level.block.TintedGlassBlock; +import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.BlockHitResult; import net.minecraft.world.phys.EntityHitResult; +import net.minecraft.world.phys.HitResult; import net.minecraft.world.phys.Vec3; public class PortalGunProjectileEntity extends ThrowableItemProjectile { + private static final TagKey GLASS_BLOCKS = TagKey.create(Registries.BLOCK, ResourceLocation.fromNamespaceAndPath("c", "glass_blocks")); + private static final TagKey GLASS_PANES = TagKey.create(Registries.BLOCK, ResourceLocation.fromNamespaceAndPath("c", "glass_panes")); + private static final int MAX_TICKETED_PROJECTILES = 20; + private static final int PROJECTILE_TICKET_LEVEL = 31; + private static final TicketType PROJECTILE_TICKET = TicketType.create("antarchy_portal_projectile", Comparator.comparing(UUID::toString)); + private static final LinkedHashMap PROJECTILE_TICKETS = new LinkedHashMap<>(); private static final EntityDataAccessor SIDE = SynchedEntityData.defineId(PortalGunProjectileEntity.class, EntityDataSerializers.INT); private static final EntityDataAccessor SHOOTER_ID = SynchedEntityData.defineId(PortalGunProjectileEntity.class, EntityDataSerializers.INT); private static final EntityDataAccessor DISTANCE = SynchedEntityData.defineId(PortalGunProjectileEntity.class, EntityDataSerializers.INT); - private static final EntityDataAccessor SPAWN_X = SynchedEntityData.defineId(PortalGunProjectileEntity.class, EntityDataSerializers.INT); - private static final EntityDataAccessor SPAWN_Y = SynchedEntityData.defineId(PortalGunProjectileEntity.class, EntityDataSerializers.INT); - private static final EntityDataAccessor SPAWN_Z = SynchedEntityData.defineId(PortalGunProjectileEntity.class, EntityDataSerializers.INT); + private static final EntityDataAccessor SPAWN_X = SynchedEntityData.defineId(PortalGunProjectileEntity.class, EntityDataSerializers.LONG); + private static final EntityDataAccessor SPAWN_Y = SynchedEntityData.defineId(PortalGunProjectileEntity.class, EntityDataSerializers.LONG); + private static final EntityDataAccessor SPAWN_Z = SynchedEntityData.defineId(PortalGunProjectileEntity.class, EntityDataSerializers.LONG); + private static final EntityDataAccessor MAX_TRAVEL_DISTANCE_DATA = SynchedEntityData.defineId(PortalGunProjectileEntity.class, EntityDataSerializers.INT); private static final EntityDataAccessor VELOCITY_X = SynchedEntityData.defineId(PortalGunProjectileEntity.class, EntityDataSerializers.FLOAT); private static final EntityDataAccessor VELOCITY_Y = SynchedEntityData.defineId(PortalGunProjectileEntity.class, EntityDataSerializers.FLOAT); private static final EntityDataAccessor VELOCITY_Z = SynchedEntityData.defineId(PortalGunProjectileEntity.class, EntityDataSerializers.FLOAT); - private static final int MAX_FLIGHT_TICKS = 80; + private static final EntityDataAccessor PROJECTILE_SPEED = SynchedEntityData.defineId(PortalGunProjectileEntity.class, EntityDataSerializers.FLOAT); + private static final EntityDataAccessor PASS_THROUGH_GLASS = SynchedEntityData.defineId(PortalGunProjectileEntity.class, EntityDataSerializers.BOOLEAN); + private static final EntityDataAccessor PASS_THROUGH_LIQUID = SynchedEntityData.defineId(PortalGunProjectileEntity.class, EntityDataSerializers.BOOLEAN); + public static final double MIN_LAUNCH_SPEED = 4.98D; + public static final double LAUNCH_SPEED_VARIANCE = 0.02D; + public static final double MAX_TRAVEL_DISTANCE = 10000.0D; private UUID gunId; private boolean spawnSynced; + private boolean ticketRegistered; public int portalWidth = 1; public int portalHeight = 2; @@ -53,30 +85,55 @@ public class PortalGunProjectileEntity extends ThrowableItemProjectile { this.noCulling = true; } + @Override + public EntityDimensions getDimensions(Pose pose) { + return EntityDimensions.fixed(0.3F, 0.3F); + } + @Override protected void defineSynchedData(SynchedEntityData.Builder builder) { super.defineSynchedData(builder); builder.define(SIDE, PortalGunPortalEntity.PortalSide.BLUE.ordinal()); builder.define(SHOOTER_ID, -1); builder.define(DISTANCE, 0); - builder.define(SPAWN_X, 0); - builder.define(SPAWN_Y, 0); - builder.define(SPAWN_Z, 0); + builder.define(SPAWN_X, Double.doubleToRawLongBits(0.0D)); + builder.define(SPAWN_Y, Double.doubleToRawLongBits(0.0D)); + builder.define(SPAWN_Z, Double.doubleToRawLongBits(0.0D)); + builder.define(MAX_TRAVEL_DISTANCE_DATA, (int) MAX_TRAVEL_DISTANCE); builder.define(VELOCITY_X, 0.0F); builder.define(VELOCITY_Y, 0.0F); builder.define(VELOCITY_Z, 0.0F); + builder.define(PROJECTILE_SPEED, 4.99F); + builder.define(PASS_THROUGH_GLASS, false); + builder.define(PASS_THROUGH_LIQUID, false); } public void configure(PortalGunPortalEntity.PortalSide side, UUID gunId, ItemStack gunStack) { this.entityData.set(SIDE, side.ordinal()); this.gunId = gunId; this.setItem(gunStack.copyWithCount(1)); + this.portalWidth = PortalGunItem.getPortalWidth(gunStack); + this.portalHeight = PortalGunItem.getPortalHeight(gunStack); + this.entityData.set(MAX_TRAVEL_DISTANCE_DATA, AntarchySettings.portalGunMaxShootDistance()); + this.entityData.set(PASS_THROUGH_GLASS, AntarchySettings.portalGunCanFireThroughGlass()); + this.entityData.set(PASS_THROUGH_LIQUID, AntarchySettings.portalGunCanFireThroughLiquid()); Entity owner = this.getOwner(); this.entityData.set(SHOOTER_ID, owner == null ? -1 : owner.getId()); - this.syncSpawnPosition(this.blockPosition()); + } + + public void syncLaunchState() { + this.syncSpawnPosition(this.position()); this.syncVelocity(); } + public void setProjectileSpeed(double speed) { + this.entityData.set(PROJECTILE_SPEED, (float) speed); + } + + public double getProjectileSpeed() { + return this.entityData.get(PROJECTILE_SPEED); + } + public PortalGunPortalEntity.PortalSide getPortalSide() { return this.entityData.get(SIDE) == PortalGunPortalEntity.PortalSide.ORANGE.ordinal() ? PortalGunPortalEntity.PortalSide.ORANGE : PortalGunPortalEntity.PortalSide.BLUE; } @@ -89,8 +146,16 @@ public class PortalGunProjectileEntity extends ThrowableItemProjectile { return this.entityData.get(DISTANCE); } - public BlockPos getSyncedSpawnPos() { - return new BlockPos(this.entityData.get(SPAWN_X), this.entityData.get(SPAWN_Y), this.entityData.get(SPAWN_Z)); + public int getMaxTravelDistance() { + return this.entityData.get(MAX_TRAVEL_DISTANCE_DATA); + } + + public Vec3 getSyncedSpawnPosition() { + return new Vec3( + Double.longBitsToDouble(this.entityData.get(SPAWN_X)), + Double.longBitsToDouble(this.entityData.get(SPAWN_Y)), + Double.longBitsToDouble(this.entityData.get(SPAWN_Z)) + ); } public Vec3 getSyncedVelocity() { @@ -124,22 +189,82 @@ public class PortalGunProjectileEntity extends ThrowableItemProjectile { @Override public void tick() { - super.tick(); - if (!this.spawnSynced) { - this.syncSpawnPosition(this.blockPosition()); + if (this.level().isClientSide && !this.spawnSynced) { + Vec3 spawnPosition = this.getSyncedSpawnPosition(); + this.setPos(spawnPosition.x, spawnPosition.y, spawnPosition.z); + this.setDeltaMovement(this.getSyncedVelocity()); + this.spawnSynced = true; + } + if (!this.level().isClientSide && !this.isRemoved()) { + this.updateProjectileTicket(); + } + this.baseTick(); + if (this.isRemoved()) { + return; + } + if (!this.spawnSynced && !this.level().isClientSide) { + this.syncSpawnPosition(this.position()); + this.syncVelocity(); + this.spawnSynced = true; } - this.syncVelocity(); this.entityData.set(DISTANCE, this.tickCount); - if (this.tickCount > MAX_FLIGHT_TICKS) { + int maxTravelDistance = this.getMaxTravelDistance(); + Vec3 spawnPosition = this.getSyncedSpawnPosition(); + if (this.tickCount > maxTravelDistance / 5.0D + 10.0D + || this.position().distanceToSqr(spawnPosition) > (maxTravelDistance + 5.0D) * (maxTravelDistance + 5.0D)) { this.discard(); return; } Vec3 motion = this.getDeltaMovement(); + Vec3 start = this.position(); + Vec3 end = start.add(motion); + ClipContext.Fluid fluidMode = this.entityData.get(PASS_THROUGH_LIQUID) ? ClipContext.Fluid.NONE : ClipContext.Fluid.ANY; + HitResult hit = this.level().clip(new ClipContext(start, end, ClipContext.Block.COLLIDER, fluidMode, this)); + if (hit instanceof BlockHitResult blockHit) { + BlockPos hitBlockPos = blockHit.getBlockPos(); + net.minecraft.world.level.block.state.BlockState hitBlockState = this.level().getBlockState(hitBlockPos); + boolean passThroughGlass = this.shouldPassThroughGlass(blockHit); + if (!hitBlockState.isAir()) { + hitBlockState.entityInside(this.level(), hitBlockPos, this); + if (this.isRemoved()) { + return; + } + } + if (!passThroughGlass) { + this.onHit(blockHit); + if (this.isRemoved()) { + return; + } + } + this.setPos(blockHit.getLocation().subtract(motion.scale(0.98D))); + } if (motion.lengthSqr() > 1.0E-6D) { double horizontal = Math.sqrt(motion.x * motion.x + motion.z * motion.z); this.setYRot((float) (net.minecraft.util.Mth.atan2(motion.z, motion.x) * (180.0D / Math.PI)) - 90.0F); this.setXRot((float) (-(net.minecraft.util.Mth.atan2(motion.y, horizontal) * (180.0D / Math.PI)))); } + this.setPos(this.position().add(motion)); + this.checkInsideBlocks(); + if (this.isRemoved()) { + return; + } + if (!this.level().isClientSide && !this.isRemoved()) { + this.updateProjectileTicket(); + } + if (this.isInWater()) { + for (int i = 0; i < 4; i++) { + float backtrack = 0.25F; + this.level().addParticle( + net.minecraft.core.particles.ParticleTypes.BUBBLE, + this.getX() - motion.x * backtrack, + this.getY() - motion.y * backtrack, + this.getZ() - motion.z * backtrack, + motion.x, + motion.y, + motion.z + ); + } + } if (this.level().isClientSide) { Vec3 point = this.position(); if (this.getPortalSide() == PortalGunPortalEntity.PortalSide.BLUE) { @@ -152,6 +277,63 @@ public class PortalGunProjectileEntity extends ThrowableItemProjectile { } } + @Override + public void remove(Entity.RemovalReason reason) { + this.releaseProjectileTicket(); + super.remove(reason); + } + + private void updateProjectileTicket() { + if (!(this.level() instanceof ServerLevel serverLevel)) { + return; + } + if (!AntarchySettings.portalGunCanPortalProjectilesChunkload()) { + this.releaseProjectileTicket(); + return; + } + ChunkPos chunkPos = this.chunkPosition(); + UUID ticketId = this.getUUID(); + ProjectileTicket current = PROJECTILE_TICKETS.get(ticketId); + if (current != null && current.level == serverLevel && current.chunkPos.equals(chunkPos)) { + return; + } + if (current != null) { + current.level.getChunkSource().removeRegionTicket(PROJECTILE_TICKET, current.chunkPos, PROJECTILE_TICKET_LEVEL, ticketId); + PROJECTILE_TICKETS.remove(ticketId); + } + while (PROJECTILE_TICKETS.size() >= MAX_TICKETED_PROJECTILES) { + Map.Entry oldest = PROJECTILE_TICKETS.entrySet().iterator().next(); + oldest.getValue().level.getChunkSource().removeRegionTicket(PROJECTILE_TICKET, oldest.getValue().chunkPos, PROJECTILE_TICKET_LEVEL, oldest.getKey()); + PROJECTILE_TICKETS.remove(oldest.getKey()); + } + serverLevel.getChunkSource().addRegionTicket(PROJECTILE_TICKET, chunkPos, PROJECTILE_TICKET_LEVEL, ticketId); + PROJECTILE_TICKETS.put(ticketId, new ProjectileTicket(serverLevel, chunkPos)); + this.ticketRegistered = true; + } + + private void releaseProjectileTicket() { + if (!this.ticketRegistered) { + return; + } + UUID ticketId = this.getUUID(); + ProjectileTicket ticket = PROJECTILE_TICKETS.remove(ticketId); + if (ticket != null) { + ticket.level.getChunkSource().removeRegionTicket(PROJECTILE_TICKET, ticket.chunkPos, PROJECTILE_TICKET_LEVEL, ticketId); + } + this.ticketRegistered = false; + } + + public static void releaseAllProjectileTickets() { + for (Map.Entry entry : PROJECTILE_TICKETS.entrySet()) { + ProjectileTicket ticket = entry.getValue(); + ticket.level.getChunkSource().removeRegionTicket(PROJECTILE_TICKET, ticket.chunkPos, PROJECTILE_TICKET_LEVEL, entry.getKey()); + } + PROJECTILE_TICKETS.clear(); + } + + private record ProjectileTicket(ServerLevel level, ChunkPos chunkPos) { + } + @Override protected void onHitEntity(EntityHitResult result) { } @@ -161,13 +343,30 @@ public class PortalGunProjectileEntity extends ThrowableItemProjectile { if (!this.level().isClientSide && this.level() instanceof ServerLevel serverLevel) { this.spawnImpactParticles(serverLevel, result.getLocation()); ItemStack sourceStack = this.resolveSourceStack(); - if (this.getOwner() instanceof Player player && sourceStack.getItem() instanceof PortalGunItem portalGunItem) { - portalGunItem.handlePortalImpact(serverLevel, player, sourceStack, this.getPortalSide(), result, this.position()); + if (this.getOwner() instanceof LivingEntity shooter && sourceStack.getItem() instanceof PortalGunItem portalGunItem) { + portalGunItem.handlePortalImpact(serverLevel, shooter, sourceStack, this.gunId, this.getPortalSide(), result, result.getLocation(), this.portalWidth, this.portalHeight); } } this.discard(); } + private boolean shouldPassThroughGlass(BlockHitResult hit) { + if (!this.entityData.get(PASS_THROUGH_GLASS)) { + return false; + } + BlockPos blockPos = hit.getBlockPos(); + BlockState state = this.level().getBlockState(blockPos); + Block block = state.getBlock(); + return state.is(GLASS_BLOCKS) + || state.is(GLASS_PANES) + || block == Blocks.GLASS + || block == Blocks.GLASS_PANE + || block == Blocks.TINTED_GLASS + || block instanceof StainedGlassBlock + || block instanceof StainedGlassPaneBlock + || block instanceof TintedGlassBlock; + } + private void spawnImpactParticles(ServerLevel level, Vec3 impactPos) { if (this.getPortalSide() == PortalGunPortalEntity.PortalSide.BLUE) { level.sendParticles(net.minecraft.core.particles.ParticleTypes.SOUL_FIRE_FLAME, impactPos.x, impactPos.y, impactPos.z, 10, 0.08D, 0.08D, 0.08D, 0.01D); @@ -178,10 +377,10 @@ public class PortalGunProjectileEntity extends ThrowableItemProjectile { level.sendParticles(net.minecraft.core.particles.ParticleTypes.CRIT, impactPos.x, impactPos.y, impactPos.z, 6, 0.04D, 0.04D, 0.04D, 0.01D); } - private void syncSpawnPosition(BlockPos pos) { - this.entityData.set(SPAWN_X, pos.getX()); - this.entityData.set(SPAWN_Y, pos.getY()); - this.entityData.set(SPAWN_Z, pos.getZ()); + private void syncSpawnPosition(Vec3 pos) { + this.entityData.set(SPAWN_X, Double.doubleToRawLongBits(pos.x)); + this.entityData.set(SPAWN_Y, Double.doubleToRawLongBits(pos.y)); + this.entityData.set(SPAWN_Z, Double.doubleToRawLongBits(pos.z)); this.spawnSynced = true; } @@ -207,12 +406,20 @@ public class PortalGunProjectileEntity extends ThrowableItemProjectile { tag.putInt("Side", this.entityData.get(SIDE)); tag.putInt("ShooterId", this.entityData.get(SHOOTER_ID)); tag.putInt("Distance", this.entityData.get(DISTANCE)); - tag.putInt("SpawnX", this.entityData.get(SPAWN_X)); - tag.putInt("SpawnY", this.entityData.get(SPAWN_Y)); - tag.putInt("SpawnZ", this.entityData.get(SPAWN_Z)); + Vec3 spawnPosition = this.getSyncedSpawnPosition(); + tag.putInt("SpawnX", net.minecraft.util.Mth.floor(spawnPosition.x)); + tag.putInt("SpawnY", net.minecraft.util.Mth.floor(spawnPosition.y)); + tag.putInt("SpawnZ", net.minecraft.util.Mth.floor(spawnPosition.z)); + tag.putDouble("SpawnPosX", spawnPosition.x); + tag.putDouble("SpawnPosY", spawnPosition.y); + tag.putDouble("SpawnPosZ", spawnPosition.z); + tag.putInt("MaxTravelDistance", this.entityData.get(MAX_TRAVEL_DISTANCE_DATA)); tag.putFloat("VelocityX", this.entityData.get(VELOCITY_X)); tag.putFloat("VelocityY", this.entityData.get(VELOCITY_Y)); tag.putFloat("VelocityZ", this.entityData.get(VELOCITY_Z)); + tag.putFloat("ProjectileSpeed", this.entityData.get(PROJECTILE_SPEED)); + tag.putBoolean("PassThroughGlass", this.entityData.get(PASS_THROUGH_GLASS)); + tag.putBoolean("PassThroughLiquid", this.entityData.get(PASS_THROUGH_LIQUID)); tag.putInt("PortalWidth", this.portalWidth); tag.putInt("PortalHeight", this.portalHeight); if (this.gunId != null) { @@ -226,14 +433,20 @@ public class PortalGunProjectileEntity extends ThrowableItemProjectile { this.entityData.set(SIDE, tag.getInt("Side")); this.entityData.set(SHOOTER_ID, tag.getInt("ShooterId")); this.entityData.set(DISTANCE, tag.getInt("Distance")); - this.entityData.set(SPAWN_X, tag.getInt("SpawnX")); - this.entityData.set(SPAWN_Y, tag.getInt("SpawnY")); - this.entityData.set(SPAWN_Z, tag.getInt("SpawnZ")); + this.entityData.set(SPAWN_X, Double.doubleToRawLongBits(tag.contains("SpawnPosX") ? tag.getDouble("SpawnPosX") : tag.getInt("SpawnX"))); + this.entityData.set(SPAWN_Y, Double.doubleToRawLongBits(tag.contains("SpawnPosY") ? tag.getDouble("SpawnPosY") : tag.getInt("SpawnY"))); + this.entityData.set(SPAWN_Z, Double.doubleToRawLongBits(tag.contains("SpawnPosZ") ? tag.getDouble("SpawnPosZ") : tag.getInt("SpawnZ"))); + this.entityData.set(MAX_TRAVEL_DISTANCE_DATA, tag.contains("MaxTravelDistance") + ? Math.max(1, tag.getInt("MaxTravelDistance")) + : AntarchySettings.portalGunMaxShootDistance()); this.entityData.set(VELOCITY_X, tag.getFloat("VelocityX")); this.entityData.set(VELOCITY_Y, tag.getFloat("VelocityY")); this.entityData.set(VELOCITY_Z, tag.getFloat("VelocityZ")); - this.portalWidth = Math.max(1, tag.getInt("PortalWidth")); - this.portalHeight = Math.max(2, tag.getInt("PortalHeight")); + this.entityData.set(PROJECTILE_SPEED, tag.contains("ProjectileSpeed") ? tag.getFloat("ProjectileSpeed") : 4.99F); + this.entityData.set(PASS_THROUGH_GLASS, tag.contains("PassThroughGlass") ? tag.getBoolean("PassThroughGlass") : AntarchySettings.portalGunCanFireThroughGlass()); + this.entityData.set(PASS_THROUGH_LIQUID, tag.contains("PassThroughLiquid") ? tag.getBoolean("PassThroughLiquid") : AntarchySettings.portalGunCanFireThroughLiquid()); + this.portalWidth = net.minecraft.util.Mth.clamp(tag.getInt("PortalWidth"), 1, 16); + this.portalHeight = net.minecraft.util.Mth.clamp(tag.getInt("PortalHeight"), 2, 16); this.spawnSynced = true; if (tag.hasUUID("GunId")) { this.gunId = tag.getUUID("GunId"); diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/portalgun/PortalGunResetManager.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/portalgun/PortalGunResetManager.java new file mode 100644 index 00000000..3a37a81f --- /dev/null +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/portalgun/PortalGunResetManager.java @@ -0,0 +1,86 @@ +package com.craisinlord.antarchy.content.portalgun; + +import com.craisinlord.antarchy.content.item.PortalGunItem; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.item.ItemStack; + +public final class PortalGunResetManager { + public static final int ARM = 0; + public static final int RELEASE = 1; + public static final int RESET_ALL = 2; + public static final int RESET_BLUE = 3; + private static final Map STATES = new HashMap<>(); + + private PortalGunResetManager() { + } + + public static void handleInput(ServerPlayer player, int action) { + ItemStack stack = heldPortalGun(player); + if (stack.isEmpty()) { + STATES.remove(player.getUUID()); + return; + } + UUID gunId = PortalGunItem.ensureGunIdentity(stack, player); + if (action == ARM) { + STATES.put(player.getUUID(), new ResetState(gunId, player.serverLevel().getGameTime(), false)); + return; + } + ResetState state = activeState(player, gunId); + if (action == RESET_ALL) { + PortalGunItem item = (PortalGunItem) stack.getItem(); + item.resetPortals(player, stack); + STATES.remove(player.getUUID()); + } else if (action == RESET_BLUE && state != null && !state.sideReset()) { + ((PortalGunItem) stack.getItem()).resetPortalSide(player, stack, PortalGunPortalEntity.PortalSide.BLUE); + STATES.put(player.getUUID(), new ResetState(gunId, state.startedAt(), true)); + } else if (action == RELEASE) { + STATES.remove(player.getUUID()); + } + } + + public static boolean handleOrangeReset(ServerPlayer player, ItemStack stack) { + if (!(stack.getItem() instanceof PortalGunItem)) { + return false; + } + UUID gunId = PortalGunItem.getGunId(stack); + ResetState state = activeState(player, gunId); + if (state == null) { + return false; + } + if (!state.sideReset()) { + ((PortalGunItem) stack.getItem()).resetPortalSide(player, stack, PortalGunPortalEntity.PortalSide.ORANGE); + STATES.put(player.getUUID(), new ResetState(gunId, state.startedAt(), true)); + } + return true; + } + + public static void clear(MinecraftServer server) { + STATES.clear(); + } + + private static ResetState activeState(ServerPlayer player, UUID gunId) { + ResetState state = STATES.get(player.getUUID()); + if (state == null || gunId == null || !state.gunId().equals(gunId) + || player.serverLevel().getGameTime() - state.startedAt() > 100L) { + STATES.remove(player.getUUID()); + return null; + } + return state; + } + + private static ItemStack heldPortalGun(ServerPlayer player) { + ItemStack main = player.getMainHandItem(); + if (main.getItem() instanceof PortalGunItem) { + return main; + } + ItemStack offhand = player.getOffhandItem(); + return offhand.getItem() instanceof PortalGunItem ? offhand : ItemStack.EMPTY; + } + + private record ResetState(UUID gunId, long startedAt, boolean sideReset) { + } +} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/portalgun/PortalGunSavedData.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/portalgun/PortalGunSavedData.java index f636bb99..35620513 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/portalgun/PortalGunSavedData.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/portalgun/PortalGunSavedData.java @@ -1,12 +1,17 @@ package com.craisinlord.antarchy.content.portalgun; import java.util.HashMap; +import java.util.HashSet; +import java.nio.charset.StandardCharsets; import java.util.Map; +import java.util.Objects; import java.util.Optional; +import java.util.Set; import java.util.UUID; import net.minecraft.core.HolderLookup; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.ListTag; +import net.minecraft.resources.ResourceLocation; import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ServerLevel; import net.minecraft.world.entity.Entity; @@ -16,9 +21,14 @@ public final class PortalGunSavedData extends SavedData { private static final String ID = "antarchy_portal_gun_pairs"; private static final String ENTRIES_KEY = "Entries"; private static final String OWNER_KEY = "Owner"; + private static final String DIMENSION_KEY = "Dimension"; + private static final String GUN_KEY = "Gun"; private static final String BLUE_KEY = "Blue"; private static final String ORANGE_KEY = "Orange"; - private final Map pairs = new HashMap<>(); + private static final String CHANNELS_KEY = "Channels"; + private final Map pairs = new HashMap<>(); + private final Map legacyPairs = new HashMap<>(); + private final Map channelGunIds = new HashMap<>(); public static PortalGunSavedData create() { return new PortalGunSavedData(); @@ -31,7 +41,20 @@ public final class PortalGunSavedData extends SavedData { UUID owner = entryTag.getUUID(OWNER_KEY); UUID blue = entryTag.hasUUID(BLUE_KEY) ? entryTag.getUUID(BLUE_KEY) : null; UUID orange = entryTag.hasUUID(ORANGE_KEY) ? entryTag.getUUID(ORANGE_KEY) : null; - data.pairs.put(owner, new PairRecord(blue, orange)); + PairRecord record = new PairRecord(blue, orange); + ResourceLocation dimension = entryTag.contains(DIMENSION_KEY) ? ResourceLocation.tryParse(entryTag.getString(DIMENSION_KEY)) : null; + if (dimension == null) { + data.legacyPairs.put(owner, record); + } else { + UUID gunId = entryTag.hasUUID(GUN_KEY) ? entryTag.getUUID(GUN_KEY) : null; + data.pairs.put(new PairKey(owner, dimension, gunId), record); + } + } + for (net.minecraft.nbt.Tag rawChannel : tag.getList(CHANNELS_KEY, net.minecraft.nbt.Tag.TAG_COMPOUND)) { + CompoundTag channelTag = (CompoundTag) rawChannel; + if (channelTag.hasUUID(OWNER_KEY) && channelTag.hasUUID(GUN_KEY) && channelTag.contains("Channel")) { + data.channelGunIds.put(new OwnerChannelKey(channelTag.getUUID(OWNER_KEY), channelTag.getString("Channel")), channelTag.getUUID(GUN_KEY)); + } } return data; } @@ -39,21 +62,44 @@ public final class PortalGunSavedData extends SavedData { @Override public CompoundTag save(CompoundTag tag, HolderLookup.Provider registries) { ListTag entries = new ListTag(); - for (Map.Entry entry : this.pairs.entrySet()) { + for (Map.Entry entry : this.pairs.entrySet()) { CompoundTag entryTag = new CompoundTag(); - entryTag.putUUID(OWNER_KEY, entry.getKey()); - if (entry.getValue().bluePortalId() != null) { - entryTag.putUUID(BLUE_KEY, entry.getValue().bluePortalId()); - } - if (entry.getValue().orangePortalId() != null) { - entryTag.putUUID(ORANGE_KEY, entry.getValue().orangePortalId()); + entryTag.putUUID(OWNER_KEY, entry.getKey().owner()); + entryTag.putString(DIMENSION_KEY, entry.getKey().dimension().toString()); + if (entry.getKey().gunId() != null) { + entryTag.putUUID(GUN_KEY, entry.getKey().gunId()); } + writePair(entryTag, entry.getValue()); entries.add(entryTag); } + for (Map.Entry entry : this.legacyPairs.entrySet()) { + CompoundTag entryTag = new CompoundTag(); + entryTag.putUUID(OWNER_KEY, entry.getKey()); + writePair(entryTag, entry.getValue()); + entries.add(entryTag); + } + ListTag channels = new ListTag(); + for (Map.Entry entry : this.channelGunIds.entrySet()) { + CompoundTag channelTag = new CompoundTag(); + channelTag.putUUID(OWNER_KEY, entry.getKey().owner()); + channelTag.putString("Channel", entry.getKey().channelName()); + channelTag.putUUID(GUN_KEY, entry.getValue()); + channels.add(channelTag); + } tag.put(ENTRIES_KEY, entries); + tag.put(CHANNELS_KEY, channels); return tag; } + private static void writePair(CompoundTag tag, PairRecord record) { + if (record.bluePortalId() != null) { + tag.putUUID(BLUE_KEY, record.bluePortalId()); + } + if (record.orangePortalId() != null) { + tag.putUUID(ORANGE_KEY, record.orangePortalId()); + } + } + private static PortalGunSavedData get(MinecraftServer server) { return server.overworld().getDataStorage().computeIfAbsent( new SavedData.Factory<>(PortalGunSavedData::create, PortalGunSavedData::load, null), @@ -61,35 +107,95 @@ public final class PortalGunSavedData extends SavedData { ); } - public static Optional getPortalId(MinecraftServer server, UUID owner, PortalGunPortalEntity.PortalSide side) { - PairRecord record = get(server).pairs.get(owner); + public static Optional getPortalId(MinecraftServer server, UUID owner, UUID gunId, String channelName, PortalGunPortalEntity.PortalSide side, ResourceLocation dimension) { + PortalGunSavedData data = get(server); + UUID resolvedGunId = data.resolveChannelGunId(owner, channelName, gunId); + data.migrateLegacyPair(server, owner); + data.adoptLegacyPairs(server, owner, resolvedGunId, channelName); + PairRecord record = data.pairs.get(new PairKey(owner, dimension, resolvedGunId)); if (record == null) { return Optional.empty(); } + data.synchronizeLoadedPortal(server, owner, dimension, record.bluePortalId(), resolvedGunId, channelName); + data.synchronizeLoadedPortal(server, owner, dimension, record.orangePortalId(), resolvedGunId, channelName); return Optional.ofNullable(side == PortalGunPortalEntity.PortalSide.BLUE ? record.bluePortalId() : record.orangePortalId()); } - public static boolean isRegistered(MinecraftServer server, UUID owner, PortalGunPortalEntity.PortalSide side, UUID portalId) { - return getPortalId(server, owner, side).map(portalId::equals).orElse(false); + public static Optional getPortalId(MinecraftServer server, UUID owner, UUID gunId, PortalGunPortalEntity.PortalSide side, ResourceLocation dimension) { + return getPortalId(server, owner, gunId, String.valueOf(gunId), side, dimension); } - public static void setPortal(MinecraftServer server, UUID owner, PortalGunPortalEntity.PortalSide side, UUID portalId) { + public static Optional getGunIdForPortal(MinecraftServer server, UUID owner, ResourceLocation dimension, UUID portalId) { PortalGunSavedData data = get(server); - PairRecord current = data.pairs.getOrDefault(owner, new PairRecord(null, null)); + data.migrateLegacyPair(server, owner); + for (Map.Entry entry : data.pairs.entrySet()) { + PairKey key = entry.getKey(); + if (key.owner().equals(owner) && key.dimension().equals(dimension) && key.gunId() != null + && (portalId.equals(entry.getValue().bluePortalId()) || portalId.equals(entry.getValue().orangePortalId()))) { + return Optional.of(key.gunId()); + } + } + return Optional.empty(); + } + + public static Optional getChannelNameForPortal(MinecraftServer server, UUID owner, ResourceLocation dimension, UUID portalId) { + PortalGunSavedData data = get(server); + for (Map.Entry entry : data.pairs.entrySet()) { + PairKey key = entry.getKey(); + if (!key.owner().equals(owner) || !key.dimension().equals(dimension) + || (!portalId.equals(entry.getValue().bluePortalId()) && !portalId.equals(entry.getValue().orangePortalId()))) { + continue; + } + return data.channelGunIds.entrySet().stream() + .filter(channel -> channel.getKey().owner().equals(owner) && channel.getValue().equals(key.gunId())) + .map(channel -> channel.getKey().channelName()) + .findFirst(); + } + return Optional.empty(); + } + + public static boolean isRegistered(ServerLevel level, UUID owner, UUID gunId, PortalGunPortalEntity.PortalSide side, UUID portalId) { + return isRegistered(level, owner, gunId, String.valueOf(gunId), side, portalId); + } + + public static boolean isRegistered(ServerLevel level, UUID owner, UUID gunId, String channelName, PortalGunPortalEntity.PortalSide side, UUID portalId) { + return getPortalId(level.getServer(), owner, gunId, channelName, side, level.dimension().location()).map(portalId::equals).orElse(false); + } + + public static void setPortal(ServerLevel level, UUID owner, UUID gunId, PortalGunPortalEntity.PortalSide side, UUID portalId) { + setPortal(level, owner, gunId, String.valueOf(gunId), side, portalId); + } + + public static void setPortal(ServerLevel level, UUID owner, UUID gunId, String channelName, PortalGunPortalEntity.PortalSide side, UUID portalId) { + PortalGunSavedData data = get(level.getServer()); + ResourceLocation dimension = level.dimension().location(); + UUID resolvedGunId = data.resolveChannelGunId(owner, channelName, gunId); + data.migrateLegacyPair(level.getServer(), owner); + data.adoptLegacyPairs(level.getServer(), owner, resolvedGunId, channelName); + PairKey key = new PairKey(owner, dimension, resolvedGunId); + PairRecord current = data.pairs.getOrDefault(key, new PairRecord(null, null)); PairRecord updated = side == PortalGunPortalEntity.PortalSide.BLUE ? new PairRecord(portalId, current.orangePortalId()) : new PairRecord(current.bluePortalId(), portalId); if (updated.bluePortalId() == null && updated.orangePortalId() == null) { - data.pairs.remove(owner); + data.pairs.remove(key); } else { - data.pairs.put(owner, updated); + data.pairs.put(key, updated); } data.setDirty(); } - public static void clearPortal(MinecraftServer server, UUID owner, PortalGunPortalEntity.PortalSide side, UUID portalId) { + public static void clearPortal(MinecraftServer server, UUID owner, UUID gunId, PortalGunPortalEntity.PortalSide side, UUID portalId, ResourceLocation dimension) { + clearPortal(server, owner, gunId, String.valueOf(gunId), side, portalId, dimension); + } + + public static void clearPortal(MinecraftServer server, UUID owner, UUID gunId, String channelName, PortalGunPortalEntity.PortalSide side, UUID portalId, ResourceLocation dimension) { PortalGunSavedData data = get(server); - PairRecord current = data.pairs.get(owner); + UUID resolvedGunId = data.resolveChannelGunId(owner, channelName, gunId); + data.migrateLegacyPair(server, owner); + data.adoptLegacyPairs(server, owner, resolvedGunId, channelName); + PairKey key = new PairKey(owner, dimension, resolvedGunId); + PairRecord current = data.pairs.get(key); if (current == null) { return; } @@ -102,50 +208,249 @@ public final class PortalGunSavedData extends SavedData { orange = null; } if (blue == null && orange == null) { - data.pairs.remove(owner); + data.pairs.remove(key); } else { - data.pairs.put(owner, new PairRecord(blue, orange)); + data.pairs.put(key, new PairRecord(blue, orange)); } data.setDirty(); } - public static void clearAllPortals(MinecraftServer server, UUID owner) { + public static void clearAllPortals(ServerLevel level, UUID owner, UUID gunId) { + clearAllPortals(level, owner, gunId, String.valueOf(gunId)); + } + + public static void clearAllPortals(ServerLevel level, UUID owner, UUID gunId, String channelName) { + MinecraftServer server = level.getServer(); PortalGunSavedData data = get(server); - PairRecord current = data.pairs.remove(owner); - data.setDirty(); - if (current == null) { - return; + UUID resolvedGunId = data.resolveChannelGunId(owner, channelName, gunId); + data.migrateLegacyPair(server, owner); + data.adoptLegacyPairs(server, owner, resolvedGunId, channelName); + Set portalIds = new HashSet<>(); + PairRecord legacy = gunId == null ? data.legacyPairs.remove(owner) : null; + if (legacy != null) { + addPortalIds(portalIds, legacy); + } + data.pairs.entrySet().removeIf(entry -> { + if (!entry.getKey().owner().equals(owner) || !entry.getKey().dimension().equals(level.dimension().location()) + || !Objects.equals(entry.getKey().gunId(), resolvedGunId)) { + return false; + } + addPortalIds(portalIds, entry.getValue()); + return true; + }); + data.setDirty(); + for (UUID portalId : portalIds) { + discardIfLoaded(server, portalId); } - discardIfLoaded(server, current.bluePortalId()); - discardIfLoaded(server, current.orangePortalId()); } - public static PortalGunPortalEntity findLoadedPortal(MinecraftServer server, UUID owner, PortalGunPortalEntity.PortalSide side) { - Optional portalId = getPortalId(server, owner, side); + public static void clearPortalSide(ServerLevel level, UUID owner, UUID gunId, PortalGunPortalEntity.PortalSide side) { + clearPortalSide(level, owner, gunId, String.valueOf(gunId), side); + } + + public static void clearPortalSide(ServerLevel level, UUID owner, UUID gunId, String channelName, PortalGunPortalEntity.PortalSide side) { + MinecraftServer server = level.getServer(); + PortalGunSavedData data = get(server); + UUID resolvedGunId = data.resolveChannelGunId(owner, channelName, gunId); + data.migrateLegacyPair(server, owner); + data.adoptLegacyPairs(server, owner, resolvedGunId, channelName); + Set portalIds = new HashSet<>(); + for (Map.Entry entry : new HashMap<>(data.pairs).entrySet()) { + PairKey key = entry.getKey(); + if (!key.owner().equals(owner) || !key.dimension().equals(level.dimension().location()) || !Objects.equals(key.gunId(), resolvedGunId)) { + continue; + } + PairRecord pair = entry.getValue(); + UUID removed = side == PortalGunPortalEntity.PortalSide.BLUE ? pair.bluePortalId() : pair.orangePortalId(); + if (removed == null) { + continue; + } + portalIds.add(removed); + PairRecord remaining = side == PortalGunPortalEntity.PortalSide.BLUE + ? new PairRecord(null, pair.orangePortalId()) + : new PairRecord(pair.bluePortalId(), null); + if (remaining.bluePortalId() == null && remaining.orangePortalId() == null) { + data.pairs.remove(key); + } else { + data.pairs.put(key, remaining); + } + } + data.setDirty(); + for (UUID portalId : portalIds) { + discardIfLoaded(server, portalId); + } + } + + public static PortalGunPortalEntity findLoadedPortal(ServerLevel level, UUID owner, UUID gunId, PortalGunPortalEntity.PortalSide side) { + Optional portalId = getPortalId(level.getServer(), owner, gunId, side, level.dimension().location()); if (portalId.isEmpty()) { return null; } + Entity entity = level.getEntity(portalId.get()); + return entity instanceof PortalGunPortalEntity portal && !portal.isRemoved() ? portal : null; + } + + public static PortalGunPortalEntity findLoadedPortal(ServerLevel level, UUID owner, UUID gunId, String channelName, PortalGunPortalEntity.PortalSide side) { + Optional portalId = getPortalId(level.getServer(), owner, gunId, channelName, side, level.dimension().location()); + if (portalId.isEmpty()) { + return null; + } + Entity entity = level.getEntity(portalId.get()); + return entity instanceof PortalGunPortalEntity portal && !portal.isRemoved() ? portal : null; + } + + private UUID resolveChannelGunId(UUID owner, String channelName, UUID gunId) { + if (channelName == null || channelName.isEmpty()) { + return gunId; + } + OwnerChannelKey key = new OwnerChannelKey(owner, channelName); + UUID existing = this.channelGunIds.get(key); + if (existing != null) { + return existing; + } + UUID channelGunId = UUID.nameUUIDFromBytes((owner + "\u0000" + channelName).getBytes(StandardCharsets.UTF_8)); + this.channelGunIds.put(key, channelGunId); + if (gunId != null && !gunId.equals(channelGunId)) { + this.migrateGunPairs(owner, gunId, channelGunId); + } + this.setDirty(); + return channelGunId; + } + + private void migrateGunPairs(UUID owner, UUID previousGunId, UUID channelGunId) { + for (Map.Entry entry : new HashMap<>(this.pairs).entrySet()) { + PairKey previousKey = entry.getKey(); + if (!previousKey.owner().equals(owner) || !previousGunId.equals(previousKey.gunId())) { + continue; + } + PairKey channelKey = new PairKey(owner, previousKey.dimension(), channelGunId); + PairRecord existing = this.pairs.get(channelKey); + PairRecord migrated = mergePairs(existing, entry.getValue()); + this.pairs.remove(previousKey); + this.pairs.put(channelKey, migrated); + } + } + + private static PairRecord mergePairs(PairRecord preferred, PairRecord fallback) { + if (preferred == null) { + return fallback; + } + return new PairRecord( + preferred.bluePortalId() == null ? fallback.bluePortalId() : preferred.bluePortalId(), + preferred.orangePortalId() == null ? fallback.orangePortalId() : preferred.orangePortalId() + ); + } + + private void migrateLegacyPair(MinecraftServer server, UUID owner) { + PairRecord legacy = this.legacyPairs.get(owner); + if (legacy == null) { + return; + } + ServerLevel blueLevel = findPortalLevel(server, legacy.bluePortalId()); + ServerLevel orangeLevel = findPortalLevel(server, legacy.orangePortalId()); + UUID legacyBlue = legacy.bluePortalId(); + UUID legacyOrange = legacy.orangePortalId(); + if (blueLevel != null) { + Entity blueEntity = blueLevel.getEntity(legacyBlue); + UUID blueGunId = blueEntity instanceof PortalGunPortalEntity portal ? portal.getGunId() : null; + PairKey key = new PairKey(owner, blueLevel.dimension().location(), blueGunId); + this.pairs.put(key, withPortal(this.pairs.get(key), PortalGunPortalEntity.PortalSide.BLUE, legacyBlue)); + legacyBlue = null; + } + if (orangeLevel != null) { + Entity orangeEntity = orangeLevel.getEntity(legacyOrange); + UUID orangeGunId = orangeEntity instanceof PortalGunPortalEntity portal ? portal.getGunId() : null; + PairKey key = new PairKey(owner, orangeLevel.dimension().location(), orangeGunId); + this.pairs.put(key, withPortal(this.pairs.get(key), PortalGunPortalEntity.PortalSide.ORANGE, legacyOrange)); + legacyOrange = null; + } + if (!Objects.equals(legacy.bluePortalId(), legacyBlue) || !Objects.equals(legacy.orangePortalId(), legacyOrange)) { + if (legacyBlue == null && legacyOrange == null) { + this.legacyPairs.remove(owner); + } else { + this.legacyPairs.put(owner, new PairRecord(legacyBlue, legacyOrange)); + } + this.setDirty(); + } + } + + private void adoptLegacyPairs(MinecraftServer server, UUID owner, UUID gunId, String channelName) { + if (gunId == null) { + return; + } + boolean changed = false; + for (Map.Entry entry : new HashMap<>(this.pairs).entrySet()) { + PairKey legacyKey = entry.getKey(); + if (!legacyKey.owner().equals(owner) || legacyKey.gunId() != null) { + continue; + } + PairKey ownedKey = new PairKey(owner, legacyKey.dimension(), gunId); + if (this.pairs.containsKey(ownedKey)) { + continue; + } + this.pairs.remove(legacyKey); + this.pairs.put(ownedKey, entry.getValue()); + synchronizeLoadedPortal(server, owner, legacyKey.dimension(), entry.getValue().bluePortalId(), gunId, channelName); + synchronizeLoadedPortal(server, owner, legacyKey.dimension(), entry.getValue().orangePortalId(), gunId, channelName); + changed = true; + } + if (changed) { + this.setDirty(); + } + } + + private static void synchronizeLoadedPortal(MinecraftServer server, UUID owner, ResourceLocation dimension, UUID portalId, UUID gunId, String channelName) { + if (portalId == null) { + return; + } + ServerLevel level = server.getLevel(net.minecraft.resources.ResourceKey.create(net.minecraft.core.registries.Registries.DIMENSION, dimension)); + if (level == null || !(level.getEntity(portalId) instanceof PortalGunPortalEntity portal) || !owner.equals(portal.getOwnerId())) { + return; + } + portal.adoptChannelIdentity(gunId, channelName); + } + + private static PairRecord withPortal(PairRecord current, PortalGunPortalEntity.PortalSide side, UUID portalId) { + PairRecord pair = current == null ? new PairRecord(null, null) : current; + return side == PortalGunPortalEntity.PortalSide.BLUE + ? new PairRecord(pair.bluePortalId() == null ? portalId : pair.bluePortalId(), pair.orangePortalId()) + : new PairRecord(pair.bluePortalId(), pair.orangePortalId() == null ? portalId : pair.orangePortalId()); + } + + private static ServerLevel findPortalLevel(MinecraftServer server, UUID portalId) { + if (portalId == null) { + return null; + } for (ServerLevel level : server.getAllLevels()) { - Entity entity = level.getEntity(portalId.get()); - if (entity instanceof PortalGunPortalEntity portal && !portal.isRemoved()) { - return portal; + if (level.getEntity(portalId) instanceof PortalGunPortalEntity portal && !portal.isRemoved()) { + return level; } } return null; } + private static void addPortalIds(Set portalIds, PairRecord pair) { + if (pair.bluePortalId() != null) { + portalIds.add(pair.bluePortalId()); + } + if (pair.orangePortalId() != null) { + portalIds.add(pair.orangePortalId()); + } + } + private static void discardIfLoaded(MinecraftServer server, UUID portalId) { - if (portalId == null) { - return; - } - for (ServerLevel level : server.getAllLevels()) { - if (level.getEntity(portalId) instanceof PortalGunPortalEntity portal) { - portal.discard(); - return; - } + ServerLevel level = findPortalLevel(server, portalId); + if (level != null && level.getEntity(portalId) instanceof PortalGunPortalEntity portal) { + portal.discard(); } } + private record PairKey(UUID owner, ResourceLocation dimension, UUID gunId) { + } + + private record OwnerChannelKey(UUID owner, String channelName) { + } + private record PairRecord(UUID bluePortalId, UUID orangePortalId) { } } diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/portalgun/PortalGunTransformUtil.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/portalgun/PortalGunTransformUtil.java index 3ad4ec8b..f6f69546 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/portalgun/PortalGunTransformUtil.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/portalgun/PortalGunTransformUtil.java @@ -14,19 +14,19 @@ public final class PortalGunTransformUtil { } public static Quaternionf createTransform(PortalGunPortalEntity sourcePortal, PortalGunPortalEntity destinationPortal) { - return createTransform(sourcePortal.getWidthVec(), sourcePortal.getUpVec(), sourcePortal.getNormalVec().scale(-1.0D), destinationPortal.getWidthVec(), destinationPortal.getUpVec(), destinationPortal.getNormalVec()); + return createTransform(sourcePortal.getWidthVec(), sourcePortal.getUpVec(), sourcePortal.getNormalVec().scale(-1.0D), destinationPortal.getWidthVec().scale(-1.0D), destinationPortal.getUpVec(), destinationPortal.getNormalVec()); } public static Quaternionf createTransform(Vec3 sourceWidth, Vec3 sourceUp, Vec3 sourceForward, Vec3 destinationWidth, Vec3 destinationUp, Vec3 destinationForward) { Matrix3f sourceBasis = new Matrix3f( - (float) sourceWidth.x, (float) sourceUp.x, (float) sourceForward.x, - (float) sourceWidth.y, (float) sourceUp.y, (float) sourceForward.y, - (float) sourceWidth.z, (float) sourceUp.z, (float) sourceForward.z + (float) sourceWidth.x, (float) sourceWidth.y, (float) sourceWidth.z, + (float) sourceUp.x, (float) sourceUp.y, (float) sourceUp.z, + (float) sourceForward.x, (float) sourceForward.y, (float) sourceForward.z ); Matrix3f destinationBasis = new Matrix3f( - (float) destinationWidth.x, (float) destinationUp.x, (float) destinationForward.x, - (float) destinationWidth.y, (float) destinationUp.y, (float) destinationForward.y, - (float) destinationWidth.z, (float) destinationUp.z, (float) destinationForward.z + (float) destinationWidth.x, (float) destinationWidth.y, (float) destinationWidth.z, + (float) destinationUp.x, (float) destinationUp.y, (float) destinationUp.z, + (float) destinationForward.x, (float) destinationForward.y, (float) destinationForward.z ); Matrix3f transform = destinationBasis.mul(sourceBasis.transpose(new Matrix3f()), new Matrix3f()); return transform.getNormalizedRotation(new Quaternionf()); @@ -48,9 +48,9 @@ public final class PortalGunTransformUtil { Vec3 destinationWidth = destinationPortal.getWidthVec().normalize(); Vec3 destinationUp = destinationPortal.getUpVec().normalize(); Vec3 destinationNormal = destinationPortal.getNormalVec().normalize(); - return destinationWidth.scale(relativePosition.dot(sourceWidth)) + return destinationWidth.scale(-relativePosition.dot(sourceWidth)) .add(destinationUp.scale(relativePosition.dot(sourceUp))) - .add(destinationNormal.scale(relativePosition.dot(sourceNormal))); + .add(destinationNormal.scale(-relativePosition.dot(sourceNormal))); } public static Vec3 transformVector(PortalGunPortalEntity sourcePortal, PortalGunPortalEntity destinationPortal, Vec3 vector) { @@ -60,7 +60,7 @@ public final class PortalGunTransformUtil { Vec3 destinationWidth = destinationPortal.getWidthVec().normalize(); Vec3 destinationUp = destinationPortal.getUpVec().normalize(); Vec3 destinationOut = destinationPortal.getNormalVec().normalize(); - return destinationWidth.scale(vector.dot(sourceWidth)) + return destinationWidth.scale(-vector.dot(sourceWidth)) .add(destinationUp.scale(vector.dot(sourceUp))) .add(destinationOut.scale(vector.dot(sourceInto))); } @@ -102,11 +102,11 @@ public final class PortalGunTransformUtil { public static Quaternionf orientationQuaternion(Vec3 look, Vec3 up) { Vec3 forward = look.normalize(); Vec3 correctedUp = up.subtract(forward.scale(up.dot(forward))).normalize(); - Vec3 right = correctedUp.cross(forward).normalize(); + Vec3 right = forward.cross(correctedUp).normalize(); Matrix3f basis = new Matrix3f( - (float) right.x, (float) correctedUp.x, (float) (-forward.x), - (float) right.y, (float) correctedUp.y, (float) (-forward.y), - (float) right.z, (float) correctedUp.z, (float) (-forward.z) + (float) right.x, (float) right.y, (float) right.z, + (float) correctedUp.x, (float) correctedUp.y, (float) correctedUp.z, + (float) -forward.x, (float) -forward.y, (float) -forward.z ); return basis.getNormalizedRotation(new Quaternionf()); } diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/portalgun/PortalGunWorldPortalShape.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/portalgun/PortalGunWorldPortalShape.java index a8394276..89a043d3 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/portalgun/PortalGunWorldPortalShape.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/portalgun/PortalGunWorldPortalShape.java @@ -35,6 +35,11 @@ public record PortalGunWorldPortalShape( return this.createPlaneAround(offset, this.halfDepth); } + public PortalGunWorldPortalShape scaleAperture(double scale) { + double safeScale = Math.max(0.0D, scale); + return new PortalGunWorldPortalShape(this.center, this.normal, this.up, this.right, this.halfWidth * safeScale, this.halfHeight * safeScale, this.halfDepth); + } + public Vec3[] getCorners(double normalOffset) { Vec3 center = this.center.add(this.normal.scale(normalOffset)); Vec3 width = this.right.scale(this.halfWidth); @@ -76,13 +81,7 @@ public record PortalGunWorldPortalShape( } public boolean shouldRenderFront(Vec3 cameraPos) { - PortalLocalCoords coords = this.localCoords(cameraPos); - if (coords.depth() <= -0.25D) { - return false; - } - double depthScale = Math.min(DEFAULT_SCAN_DISTANCE, Math.max(0.0D, coords.depth())); - return Math.abs(coords.horizontal()) <= this.halfWidth + depthScale - && Math.abs(coords.vertical()) <= this.halfHeight + depthScale; + return this.localCoords(cameraPos).depth() > 0.0D; } public boolean intersectsFront(AABB box, double tolerance) { @@ -128,8 +127,8 @@ public record PortalGunWorldPortalShape( double horizontalOuter = this.halfWidth + borderThickness; double verticalOuter = this.halfHeight + borderThickness; return List.of( - this.createLocalBox(-horizontalOuter, this.halfWidth, -verticalOuter, verticalOuter, -this.halfDepth, this.halfDepth), - this.createLocalBox(-this.halfWidth, horizontalOuter, -verticalOuter, verticalOuter, -this.halfDepth, this.halfDepth), + this.createLocalBox(-horizontalOuter, -this.halfWidth, -verticalOuter, verticalOuter, -this.halfDepth, this.halfDepth), + this.createLocalBox(this.halfWidth, horizontalOuter, -verticalOuter, verticalOuter, -this.halfDepth, this.halfDepth), this.createLocalBox(-this.halfWidth, this.halfWidth, -verticalOuter, -this.halfHeight, -this.halfDepth, this.halfDepth), this.createLocalBox(-this.halfWidth, this.halfWidth, this.halfHeight, verticalOuter, -this.halfDepth, this.halfDepth) ); diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/time/TimeDilationFieldEntity.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/time/TimeDilationFieldEntity.java index 0b5c166a..da939800 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/time/TimeDilationFieldEntity.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/time/TimeDilationFieldEntity.java @@ -95,6 +95,12 @@ public class TimeDilationFieldEntity extends Entity { || this.anchorId != null && this.anchorId.equals(entity.getUUID())) { return false; } + if (this.ownerId != null && this.level() instanceof ServerLevel level + && level.getEntity(this.ownerId) instanceof QueenEntity + && entity instanceof LivingEntity living + && !com.craisinlord.antarchy.content.effect.RoyalEffectEligibility.canApplyDilated(living)) { + return false; + } if (this.chronosphere && !(entity instanceof LivingEntity || entity instanceof Projectile || entity instanceof ItemEntity)) { return false; } diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/time/TimeDilationManager.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/time/TimeDilationManager.java index 5663c71b..8f4eed34 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/time/TimeDilationManager.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/time/TimeDilationManager.java @@ -146,8 +146,7 @@ public final class TimeDilationManager { if (!(entity instanceof TimeDilationEntityAccess access) || entity instanceof TimeDilationFieldEntity) { continue; } - if (entity.getType().is(com.craisinlord.antarchy.content.AntarchyTags.Entities.TIME_DILATION_IMMUNE) - || (entity instanceof net.minecraft.world.entity.LivingEntity living && !RoyalEffectEligibility.canApplyDilated(living))) { + if (entity.getType().is(com.craisinlord.antarchy.content.AntarchyTags.Entities.TIME_DILATION_IMMUNE)) { if (RoyalEffectHooks.dilatedHolder() != null && entity instanceof net.minecraft.world.entity.LivingEntity living) { living.removeEffect(RoyalEffectHooks.dilatedHolder()); } diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/worldgen/thoraxis/ThoraxisBiomeSource.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/worldgen/thoraxis/ThoraxisBiomeSource.java index 18ec058a..91f382c0 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/worldgen/thoraxis/ThoraxisBiomeSource.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/worldgen/thoraxis/ThoraxisBiomeSource.java @@ -2,9 +2,11 @@ package com.craisinlord.antarchy.content.worldgen.thoraxis; import com.craisinlord.antarchy.Antarchy; import com.mojang.serialization.Codec; +import com.mojang.serialization.DataResult; import com.mojang.serialization.MapCodec; import com.mojang.serialization.codecs.RecordCodecBuilder; import it.unimi.dsi.fastutil.longs.Long2DoubleOpenHashMap; +import java.util.List; import java.util.stream.Stream; import net.minecraft.core.BlockPos; import net.minecraft.core.Holder; @@ -56,7 +58,8 @@ public final class ThoraxisBiomeSource extends BiomeSource { Codec.INT.optionalFieldOf("dream_dunes_max_y", 160).forGetter(ThoraxisBiomeSource::dreamDunesMaxY), Codec.INT.optionalFieldOf("umbral_hills_max_y", 160).forGetter(ThoraxisBiomeSource::umbralHillsMaxY), Codec.INT.optionalFieldOf("lucid_pools_min_y", 116).forGetter(ThoraxisBiomeSource::lucidPoolsMinY), - Codec.INT.optionalFieldOf("nadir_forest_min_y", 128).forGetter(ThoraxisBiomeSource::nadirForestMinY) + Codec.INT.optionalFieldOf("nadir_forest_min_y", 128).forGetter(ThoraxisBiomeSource::nadirForestMinY), + RegionalBiomeOverlay.CODEC.listOf().optionalFieldOf("regional_overlays", List.of()).forGetter(ThoraxisBiomeSource::regionalOverlays) ).apply(instance, ThoraxisBiomeSource::new)); private final Climate.ParameterList> parameters; @@ -65,6 +68,7 @@ public final class ThoraxisBiomeSource extends BiomeSource { private final int umbralHillsMaxY; private final int lucidPoolsMinY; private final int nadirForestMinY; + private final List regionalOverlays; private final int dreamDunesMaxQuartY; private final int umbralHillsMaxQuartY; private final int lucidPoolsMinQuartY; @@ -76,19 +80,21 @@ public final class ThoraxisBiomeSource extends BiomeSource { private Holder umbralHillsBiome; private Holder lucidPoolsBiome; private Holder nadirForestBiome; + private List resolvedRegionalOverlays = List.of(); private static final ThreadLocal REGION_CACHE = ThreadLocal.withInitial(() -> new Long2DoubleOpenHashMap(512)); private static final ThreadLocal VERTICAL_CACHE = ThreadLocal.withInitial(() -> new Long2DoubleOpenHashMap(64)); - public ThoraxisBiomeSource(Climate.ParameterList> parameters, int dreamDunesMaxY, int umbralHillsMaxY, int lucidPoolsMinY, int nadirForestMinY) { + public ThoraxisBiomeSource(Climate.ParameterList> parameters, int dreamDunesMaxY, int umbralHillsMaxY, int lucidPoolsMinY, int nadirForestMinY, List regionalOverlays) { this.parameters = parameters; this.delegate = MultiNoiseBiomeSource.createFromList(parameters); this.dreamDunesMaxY = dreamDunesMaxY; this.umbralHillsMaxY = umbralHillsMaxY; this.lucidPoolsMinY = lucidPoolsMinY; this.nadirForestMinY = nadirForestMinY; + this.regionalOverlays = regionalOverlays; this.dreamDunesMaxQuartY = QuartPos.fromBlock(dreamDunesMaxY); this.umbralHillsMaxQuartY = QuartPos.fromBlock(umbralHillsMaxY); this.lucidPoolsMinQuartY = QuartPos.fromBlock(lucidPoolsMinY); @@ -109,6 +115,7 @@ public final class ThoraxisBiomeSource extends BiomeSource { this.umbralHillsBiome = this.findBiome(UMBRAL_HILLS); this.lucidPoolsBiome = this.findBiome(LUCID_POOLS); this.nadirForestBiome = this.findBiome(NADIR_FOREST); + this.resolvedRegionalOverlays = this.resolveRegionalOverlays(); this.biomesResolved = true; } } @@ -133,6 +140,10 @@ public final class ThoraxisBiomeSource extends BiomeSource { return this.umbralHillsMaxY; } + private List regionalOverlays() { + return this.regionalOverlays; + } + @Override protected MapCodec codec() { return CODEC; @@ -179,6 +190,12 @@ public final class ThoraxisBiomeSource extends BiomeSource { return this.umbralHillsBiome; } + for (ResolvedRegionalBiomeOverlay overlay : this.resolvedRegionalOverlays) { + if (region >= overlay.minNoise() && region < overlay.maxNoise()) { + return overlay.biome(); + } + } + Holder delegateBiome = this.delegate.getNoiseBiome(x, y, z, sampler); return this.nightmareWastesBiome != null ? this.nightmareWastesBiome : delegateBiome; } @@ -198,6 +215,37 @@ public final class ThoraxisBiomeSource extends BiomeSource { .orElse(null); } + private Holder findBiome(ResourceLocation location) { + ResourceKey key = ResourceKey.create(Registries.BIOME, location); + return this.findBiome(key); + } + + private List resolveRegionalOverlays() { + return this.regionalOverlays.stream().map(overlay -> { + Holder biome = this.findBiome(overlay.biome()); + if (biome == null) { + throw new IllegalStateException("Thoraxis regional overlay biome " + overlay.biome() + " must be listed in the biome source parameters"); + } + return new ResolvedRegionalBiomeOverlay(biome, overlay.minNoise(), overlay.maxNoise()); + }).toList(); + } + + private record RegionalBiomeOverlay(ResourceLocation biome, double minNoise, double maxNoise) { + private static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( + ResourceLocation.CODEC.fieldOf("biome").forGetter(RegionalBiomeOverlay::biome), + Codec.DOUBLE.fieldOf("min_noise").forGetter(RegionalBiomeOverlay::minNoise), + Codec.DOUBLE.fieldOf("max_noise").forGetter(RegionalBiomeOverlay::maxNoise) + ).apply(instance, RegionalBiomeOverlay::new)).flatXmap( + overlay -> overlay.minNoise() >= 0.0D && overlay.maxNoise() <= 1.0D && overlay.minNoise() < overlay.maxNoise() + ? DataResult.success(overlay) + : DataResult.error(() -> "Regional biome overlay noise bounds must satisfy 0 <= min_noise < max_noise <= 1"), + DataResult::success + ); + } + + private record ResolvedRegionalBiomeOverlay(Holder biome, double minNoise, double maxNoise) { + } + private double cachedRegionNoise(int quartX, int quartZ) { long key = net.minecraft.world.level.ChunkPos.asLong(quartX, quartZ); Long2DoubleOpenHashMap cache = REGION_CACHE.get(); diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/AntarchyMixinPlugin.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/AntarchyMixinPlugin.java index 6d5570af..34ae51ba 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/AntarchyMixinPlugin.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/AntarchyMixinPlugin.java @@ -1,5 +1,6 @@ package com.craisinlord.antarchy.mixins; +import com.craisinlord.antarchy.content.client.renderer.SodiumCompat; import java.util.List; import java.util.Set; import org.objectweb.asm.tree.ClassNode; @@ -28,6 +29,17 @@ public final class AntarchyMixinPlugin implements IMixinConfigPlugin { if (!classPresent(targetClassName)) { return false; } + if (SodiumCompat.isLoaded() + && (mixinClassName.endsWith(".LevelRendererPortalViewAreaAccessor") + || mixinClassName.endsWith(".LevelRendererPortalViewAreaMixin") + || mixinClassName.endsWith(".LevelRendererPortalChunkBudgetMixin") + || mixinClassName.endsWith(".PortalViewChunkCacheMixin") + || mixinClassName.endsWith(".PortalViewSectionSortingMixin"))) { + return false; + } + if (mixinClassName.endsWith(".SodiumWorldRendererMixin")) { + return SodiumCompat.isLoaded(); + } if (mixinClassName.startsWith("com.craisinlord.antarchy.mixins.compat.scguns.")) { return classPresent("top.ribs.scguns.entity.projectile.ProjectileEntity"); } diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/FallingBlockEntityAccessor.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/FallingBlockEntityAccessor.java new file mode 100644 index 00000000..06836788 --- /dev/null +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/FallingBlockEntityAccessor.java @@ -0,0 +1,11 @@ +package com.craisinlord.antarchy.mixins; + +import net.minecraft.world.entity.item.FallingBlockEntity; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(FallingBlockEntity.class) +public interface FallingBlockEntityAccessor { + @Accessor("time") + void antarchy$setTime(int time); +} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/GrindstoneMenuMixin.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/GrindstoneMenuMixin.java index 104f118d..428af84a 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/GrindstoneMenuMixin.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/GrindstoneMenuMixin.java @@ -1,6 +1,7 @@ package com.craisinlord.antarchy.mixins; import com.craisinlord.antarchy.content.item.InnateEnchantmentHelper; +import com.craisinlord.antarchy.content.item.GiantFryingPanItem; import net.minecraft.world.inventory.GrindstoneMenu; import net.minecraft.world.item.ItemStack; import org.spongepowered.asm.mixin.Mixin; @@ -19,6 +20,11 @@ public abstract class GrindstoneMenuMixin { @Inject(method = "computeResult", at = @At("RETURN"), cancellable = true) private void antarchy$blockInnateOnlyNoOp(ItemStack firstInput, ItemStack secondInput, CallbackInfoReturnable cir) { + if (firstInput.getItem() instanceof GiantFryingPanItem || secondInput.getItem() instanceof GiantFryingPanItem) { + cir.setReturnValue(ItemStack.EMPTY); + return; + } + if (!firstInput.isEmpty() && secondInput.isEmpty() && InnateEnchantmentHelper.hasInnateNonCurseEnchantments(firstInput) && !InnateEnchantmentHelper.hasRemovableNonCurseEnchantments(firstInput)) { diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/PortalGunEntityCollisionMixin.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/PortalGunEntityCollisionMixin.java index f5fa8a2e..fadcd00e 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/PortalGunEntityCollisionMixin.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/PortalGunEntityCollisionMixin.java @@ -1,7 +1,7 @@ package com.craisinlord.antarchy.mixins; -import com.craisinlord.antarchy.content.gravity.AntarchyGravityApi; import com.craisinlord.antarchy.content.portalgun.PortalGunCollisionHelper; +import com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity; import net.minecraft.world.entity.Entity; import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.Vec3; @@ -10,11 +10,37 @@ import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @Mixin(Entity.class) public abstract class PortalGunEntityCollisionMixin { @Unique private AABB antarchy$portalGunStartBox; + @Unique + private AABB antarchy$portalGunMoveStartBox; + @Unique + private Vec3 antarchy$portalGunMoveStartPosition; + + @Inject(method = "move", at = @At("HEAD")) + private void antarchy$capturePortalGunMovementStart(net.minecraft.world.entity.MoverType type, Vec3 movement, CallbackInfo ci) { + Entity entity = (Entity) (Object) this; + this.antarchy$portalGunMoveStartBox = entity.getBoundingBox(); + this.antarchy$portalGunMoveStartPosition = entity.position(); + } + + @Inject(method = "move", at = @At("RETURN")) + private void antarchy$teleportMobOnPortalCrossing(net.minecraft.world.entity.MoverType type, Vec3 movement, CallbackInfo ci) { + Entity entity = (Entity) (Object) this; + if (this.antarchy$portalGunMoveStartBox != null && this.antarchy$portalGunMoveStartPosition != null) { + PortalGunPortalEntity.teleportEntityAfterMovement( + entity, + this.antarchy$portalGunMoveStartBox, + entity.position().subtract(this.antarchy$portalGunMoveStartPosition) + ); + } + this.antarchy$portalGunMoveStartBox = null; + this.antarchy$portalGunMoveStartPosition = null; + } @Inject(method = "collide", at = @At("HEAD")) private void antarchy$capturePortalGunStartBox(Vec3 movement, CallbackInfoReturnable cir) { @@ -24,10 +50,11 @@ public abstract class PortalGunEntityCollisionMixin { @Inject(method = "collide", at = @At("RETURN"), cancellable = true) private void antarchy$restorePortalCrossingMotion(Vec3 movement, CallbackInfoReturnable cir) { Entity entity = (Entity) (Object) this; - if (this.antarchy$portalGunStartBox == null || AntarchyGravityApi.isGravityInverted(entity)) { + if (this.antarchy$portalGunStartBox == null) { return; } Vec3 adjusted = PortalGunCollisionHelper.resolveCollision(entity, this.antarchy$portalGunStartBox, movement, cir.getReturnValue()); cir.setReturnValue(adjusted); } + } diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/PortalGunZombieMixin.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/PortalGunZombieMixin.java new file mode 100644 index 00000000..bc44a124 --- /dev/null +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/PortalGunZombieMixin.java @@ -0,0 +1,29 @@ +package com.craisinlord.antarchy.mixins; + +import com.craisinlord.antarchy.content.item.PortalGunItem; +import com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.entity.monster.Zombie; +import net.minecraft.world.item.ItemStack; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(Zombie.class) +public abstract class PortalGunZombieMixin { + @Inject(method = "tick", at = @At("TAIL")) + private void antarchy$firePortalGun(CallbackInfo ci) { + Zombie zombie = (Zombie) (Object) this; + if (!(zombie.level() instanceof ServerLevel serverLevel)) { + return; + } + ItemStack stack = zombie.getMainHandItem(); + if (stack.getItem() instanceof PortalGunItem portalGun && zombie.getRandom().nextFloat() < 0.008F) { + PortalGunPortalEntity.PortalSide side = zombie.getRandom().nextBoolean() + ? PortalGunPortalEntity.PortalSide.BLUE + : PortalGunPortalEntity.PortalSide.ORANGE; + portalGun.fireMobPortal(serverLevel, zombie, stack, side); + } + } +} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/client/CameraBasisAccessor.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/client/CameraBasisAccessor.java new file mode 100644 index 00000000..abfce3eb --- /dev/null +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/client/CameraBasisAccessor.java @@ -0,0 +1,18 @@ +package com.craisinlord.antarchy.mixins.client; + +import net.minecraft.client.Camera; +import org.joml.Vector3f; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(Camera.class) +public interface CameraBasisAccessor { + @Accessor("forwards") + Vector3f antarchy$getForwards(); + + @Accessor("up") + Vector3f antarchy$getUp(); + + @Accessor("left") + Vector3f antarchy$getLeft(); +} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/client/LevelRendererPortalChunkBudgetMixin.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/client/LevelRendererPortalChunkBudgetMixin.java new file mode 100644 index 00000000..55218df1 --- /dev/null +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/client/LevelRendererPortalChunkBudgetMixin.java @@ -0,0 +1,65 @@ +package com.craisinlord.antarchy.mixins.client; + +import com.craisinlord.antarchy.content.client.renderer.PortalGunPortalRendererPool; +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.client.renderer.LevelRenderer; +import net.minecraft.client.renderer.chunk.RenderRegionCache; +import net.minecraft.client.renderer.chunk.SectionRenderDispatcher; +import net.minecraft.world.level.lighting.LevelLightEngine; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +@Mixin(LevelRenderer.class) +public abstract class LevelRendererPortalChunkBudgetMixin { + @Redirect( + method = "renderLevel", + at = @At(value = "INVOKE", target = "Lnet/minecraft/client/multiplayer/ClientLevel;pollLightUpdates()V") + ) + private void antarchy$skipProxyLightQueuePoll(ClientLevel level) { + if (!PortalGunPortalRendererPool.isProxy((LevelRenderer) (Object) this)) { + level.pollLightUpdates(); + } + } + + @Redirect( + method = "renderLevel", + at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/lighting/LevelLightEngine;runLightUpdates()I") + ) + private int antarchy$skipProxyLightQueueDrain(LevelLightEngine lightEngine) { + return PortalGunPortalRendererPool.isProxy((LevelRenderer) (Object) this) ? 0 : lightEngine.runLightUpdates(); + } + + @Redirect( + method = "compileSections", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/client/renderer/chunk/SectionRenderDispatcher;rebuildSectionSync(Lnet/minecraft/client/renderer/chunk/SectionRenderDispatcher$RenderSection;Lnet/minecraft/client/renderer/chunk/RenderRegionCache;)V" + ) + ) + private void antarchy$queuePortalSyncRebuilds(SectionRenderDispatcher dispatcher, SectionRenderDispatcher.RenderSection section, RenderRegionCache cache) { + PortalGunPortalRendererPool.rebuildSection((LevelRenderer) (Object) this, dispatcher, section, cache); + } + + @Redirect( + method = "compileSections", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/client/renderer/chunk/SectionRenderDispatcher$RenderSection;rebuildSectionAsync(Lnet/minecraft/client/renderer/chunk/SectionRenderDispatcher;Lnet/minecraft/client/renderer/chunk/RenderRegionCache;)V" + ) + ) + private void antarchy$budgetPortalAsyncRebuilds(SectionRenderDispatcher.RenderSection section, SectionRenderDispatcher dispatcher, RenderRegionCache cache) { + PortalGunPortalRendererPool.rebuildSectionAsync((LevelRenderer) (Object) this, dispatcher, section, cache); + } + + @Redirect( + method = "compileSections", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/client/renderer/chunk/SectionRenderDispatcher$RenderSection;setNotDirty()V" + ) + ) + private void antarchy$keepUnscheduledPortalSectionsDirty(SectionRenderDispatcher.RenderSection section) { + PortalGunPortalRendererPool.setNotDirty(section); + } +} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/client/LevelRendererPortalLifecycleMixin.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/client/LevelRendererPortalLifecycleMixin.java new file mode 100644 index 00000000..19921874 --- /dev/null +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/client/LevelRendererPortalLifecycleMixin.java @@ -0,0 +1,55 @@ +package com.craisinlord.antarchy.mixins.client; + +import com.craisinlord.antarchy.content.client.PortalGunPortalRenderState; +import com.craisinlord.antarchy.content.client.renderer.PortalGunPortalViewAreaManager; +import com.craisinlord.antarchy.content.client.renderer.PortalGunPortalRendererPool; +import com.craisinlord.antarchy.content.client.renderer.PortalGunPortalViewRenderer; +import com.craisinlord.antarchy.content.client.renderer.PortalStencilTarget; +import net.minecraft.client.Minecraft; +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.client.renderer.LevelRenderer; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(LevelRenderer.class) +public abstract class LevelRendererPortalLifecycleMixin { + @Inject(method = "renderLevel", at = @At("HEAD")) + private void antarchy$preparePortalStencil(CallbackInfo ci) { + if (!PortalGunPortalRendererPool.isProxy((LevelRenderer) (Object) this)) { + PortalStencilTarget.prepareMainTarget(Minecraft.getInstance().getMainRenderTarget()); + } + } + + @Inject(method = "setLevel", at = @At("HEAD")) + private void antarchy$clearPortalViewsOnLevelChange(ClientLevel level, CallbackInfo ci) { + if (PortalGunPortalRendererPool.isProxy((LevelRenderer) (Object) this)) { + return; + } + PortalGunPortalRendererPool.clear(); + PortalGunPortalViewAreaManager.clear(); + } + + @Inject(method = "close", at = @At("HEAD")) + private void antarchy$clearPortalViewsOnClose(CallbackInfo ci) { + if (PortalGunPortalRendererPool.isProxy((LevelRenderer) (Object) this)) { + return; + } + PortalGunPortalRendererPool.clear(); + PortalGunPortalViewAreaManager.clear(); + PortalGunPortalViewRenderer.releaseTargets(); + } + + @Inject(method = "allChanged", at = @At("TAIL")) + private void antarchy$clearPortalViewsOnRendererRebuild(CallbackInfo ci) { + if (PortalGunPortalRendererPool.isProxy((LevelRenderer) (Object) this)) { + return; + } + if (PortalGunPortalRenderState.getContext() == null) { + PortalGunPortalRendererPool.clear(); + PortalGunPortalViewAreaManager.clear(); + PortalGunPortalViewRenderer.releaseTargets(); + } + } +} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/client/LevelRendererPortalSceneInvoker.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/client/LevelRendererPortalSceneInvoker.java new file mode 100644 index 00000000..7789063a --- /dev/null +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/client/LevelRendererPortalSceneInvoker.java @@ -0,0 +1,34 @@ +package com.craisinlord.antarchy.mixins.client; + +import com.mojang.blaze3d.vertex.PoseStack; +import net.minecraft.client.Camera; +import net.minecraft.client.renderer.culling.Frustum; +import net.minecraft.client.renderer.LevelRenderer; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.client.renderer.LightTexture; +import net.minecraft.world.entity.Entity; +import org.joml.Matrix4f; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Invoker; + +@Mixin(LevelRenderer.class) +public interface LevelRendererPortalSceneInvoker { + @Invoker("setupRender") + void antarchy$setupPortalScene(Camera camera, Frustum frustum, boolean capturedFrustum, boolean spectator); + + @Invoker("compileSections") + void antarchy$compilePortalSections(Camera camera); + + @Invoker("renderSectionLayer") + void antarchy$renderPortalSectionLayer(RenderType renderType, double cameraX, double cameraY, double cameraZ, Matrix4f modelViewMatrix, Matrix4f projectionMatrix); + + @Invoker("renderEntity") + void antarchy$renderPortalEntity(Entity entity, double cameraX, double cameraY, double cameraZ, float partialTick, PoseStack poseStack, MultiBufferSource bufferSource); + + @Invoker("renderSnowAndRain") + void antarchy$renderPortalWeather(LightTexture lightTexture, float partialTick, double cameraX, double cameraY, double cameraZ); + + @Invoker("renderWorldBorder") + void antarchy$renderPortalWorldBorder(Camera camera); +} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/client/LevelRendererPortalTargetsAccessor.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/client/LevelRendererPortalTargetsAccessor.java new file mode 100644 index 00000000..c76d4d1f --- /dev/null +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/client/LevelRendererPortalTargetsAccessor.java @@ -0,0 +1,15 @@ +package com.craisinlord.antarchy.mixins.client; + +import net.minecraft.client.renderer.LevelRenderer; +import net.minecraft.client.renderer.PostChain; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(LevelRenderer.class) +public interface LevelRendererPortalTargetsAccessor { + @Accessor("entityEffect") + PostChain antarchy$getEntityEffect(); + + @Accessor("transparencyChain") + PostChain antarchy$getTransparencyChain(); +} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/client/LevelRendererPortalViewAreaAccessor.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/client/LevelRendererPortalViewAreaAccessor.java new file mode 100644 index 00000000..9edbabed --- /dev/null +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/client/LevelRendererPortalViewAreaAccessor.java @@ -0,0 +1,160 @@ +package com.craisinlord.antarchy.mixins.client; + +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import com.mojang.blaze3d.vertex.VertexBuffer; +import net.minecraft.client.CloudStatus; +import net.minecraft.client.renderer.LevelRenderer; +import net.minecraft.client.renderer.SectionOcclusionGraph; +import net.minecraft.client.renderer.ViewArea; +import net.minecraft.client.renderer.chunk.SectionRenderDispatcher; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.phys.Vec3; +import java.util.Set; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Mutable; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(LevelRenderer.class) +public interface LevelRendererPortalViewAreaAccessor { + @Accessor("viewArea") + ViewArea antarchy$getViewArea(); + + @Accessor("viewArea") + void antarchy$setViewArea(ViewArea viewArea); + + @Accessor("sectionOcclusionGraph") + SectionOcclusionGraph antarchy$getSectionOcclusionGraph(); + + @Mutable + @Accessor("sectionOcclusionGraph") + void antarchy$setSectionOcclusionGraph(SectionOcclusionGraph graph); + + @Accessor("visibleSections") + ObjectArrayList antarchy$getVisibleSections(); + + @Mutable + @Accessor("visibleSections") + void antarchy$setVisibleSections(ObjectArrayList sections); + + @Accessor("globalBlockEntities") + Set antarchy$getGlobalBlockEntities(); + + @Mutable + @Accessor("globalBlockEntities") + void antarchy$setGlobalBlockEntities(Set blockEntities); + + @Accessor("lastCameraSectionX") + int antarchy$getLastCameraSectionX(); + + @Accessor("lastCameraSectionX") + void antarchy$setLastCameraSectionX(int value); + + @Accessor("lastCameraSectionY") + int antarchy$getLastCameraSectionY(); + + @Accessor("lastCameraSectionY") + void antarchy$setLastCameraSectionY(int value); + + @Accessor("lastCameraSectionZ") + int antarchy$getLastCameraSectionZ(); + + @Accessor("lastCameraSectionZ") + void antarchy$setLastCameraSectionZ(int value); + + @Accessor("prevCamX") + double antarchy$getPrevCamX(); + + @Accessor("prevCamX") + void antarchy$setPrevCamX(double value); + + @Accessor("prevCamY") + double antarchy$getPrevCamY(); + + @Accessor("prevCamY") + void antarchy$setPrevCamY(double value); + + @Accessor("prevCamZ") + double antarchy$getPrevCamZ(); + + @Accessor("prevCamZ") + void antarchy$setPrevCamZ(double value); + + @Accessor("prevCamRotX") + double antarchy$getPrevCamRotX(); + + @Accessor("prevCamRotX") + void antarchy$setPrevCamRotX(double value); + + @Accessor("prevCamRotY") + double antarchy$getPrevCamRotY(); + + @Accessor("prevCamRotY") + void antarchy$setPrevCamRotY(double value); + + @Accessor("xTransparentOld") + double antarchy$getTransparentSortX(); + + @Accessor("xTransparentOld") + void antarchy$setTransparentSortX(double value); + + @Accessor("yTransparentOld") + double antarchy$getTransparentSortY(); + + @Accessor("yTransparentOld") + void antarchy$setTransparentSortY(double value); + + @Accessor("zTransparentOld") + double antarchy$getTransparentSortZ(); + + @Accessor("zTransparentOld") + void antarchy$setTransparentSortZ(double value); + + @Accessor("cloudBuffer") + VertexBuffer antarchy$getCloudBuffer(); + + @Accessor("cloudBuffer") + void antarchy$setCloudBuffer(VertexBuffer cloudBuffer); + + @Accessor("generateClouds") + boolean antarchy$getGenerateClouds(); + + @Accessor("generateClouds") + void antarchy$setGenerateClouds(boolean generateClouds); + + @Accessor("prevCloudX") + int antarchy$getPrevCloudX(); + + @Accessor("prevCloudX") + void antarchy$setPrevCloudX(int value); + + @Accessor("prevCloudY") + int antarchy$getPrevCloudY(); + + @Accessor("prevCloudY") + void antarchy$setPrevCloudY(int value); + + @Accessor("prevCloudZ") + int antarchy$getPrevCloudZ(); + + @Accessor("prevCloudZ") + void antarchy$setPrevCloudZ(int value); + + @Accessor("prevCloudColor") + Vec3 antarchy$getPrevCloudColor(); + + @Accessor("prevCloudColor") + void antarchy$setPrevCloudColor(Vec3 value); + + @Accessor("prevCloudsType") + CloudStatus antarchy$getPrevCloudsType(); + + @Accessor("prevCloudsType") + void antarchy$setPrevCloudsType(CloudStatus value); + + @Accessor("rainSoundTime") + int antarchy$getRainSoundTime(); + + @Accessor("rainSoundTime") + void antarchy$setRainSoundTime(int value); + +} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/client/LevelRendererPortalViewAreaMixin.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/client/LevelRendererPortalViewAreaMixin.java new file mode 100644 index 00000000..f150fc06 --- /dev/null +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/client/LevelRendererPortalViewAreaMixin.java @@ -0,0 +1,63 @@ +package com.craisinlord.antarchy.mixins.client; + +import com.craisinlord.antarchy.content.client.renderer.PortalGunPortalViewAreaManager; +import com.craisinlord.antarchy.content.client.PortalGunPortalRenderState; +import com.llamalad7.mixinextras.injector.ModifyExpressionValue; +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.client.renderer.LevelRenderer; +import net.minecraft.client.renderer.chunk.SectionRenderDispatcher; +import net.minecraft.core.BlockPos; +import net.minecraft.sounds.SoundEvent; +import net.minecraft.sounds.SoundSource; +import net.minecraft.world.level.ChunkPos; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(LevelRenderer.class) +public abstract class LevelRendererPortalViewAreaMixin { + @Redirect(method = "renderSnowAndRain", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/multiplayer/ClientLevel;playLocalSound(Lnet/minecraft/core/BlockPos;Lnet/minecraft/sounds/SoundEvent;Lnet/minecraft/sounds/SoundSource;FFZ)V"), require = 0) + private void antarchy$suppressPortalWeatherSound(ClientLevel level, BlockPos pos, SoundEvent sound, SoundSource source, float volume, float pitch, boolean distanceDelay) { + if (PortalGunPortalRenderState.getContext() == null) { + level.playLocalSound(pos, sound, source, volume, pitch, distanceDelay); + } + } + + @ModifyExpressionValue(method = "setupRender", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/player/LocalPlayer;getX()D"), require = 0) + private double antarchy$usePortalCameraX(double original) { + PortalGunPortalRenderState.PortalRenderContext context = PortalGunPortalRenderState.getContext(); + return context == null ? original : context.cameraPos().x; + } + + @ModifyExpressionValue(method = "setupRender", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/player/LocalPlayer;getY()D"), require = 0) + private double antarchy$usePortalCameraY(double original) { + PortalGunPortalRenderState.PortalRenderContext context = PortalGunPortalRenderState.getContext(); + return context == null ? original : context.cameraPos().y; + } + + @ModifyExpressionValue(method = "setupRender", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/player/LocalPlayer;getZ()D"), require = 0) + private double antarchy$usePortalCameraZ(double original) { + PortalGunPortalRenderState.PortalRenderContext context = PortalGunPortalRenderState.getContext(); + return context == null ? original : context.cameraPos().z; + } + + @Inject(method = "addRecentlyCompiledSection", at = @At("HEAD"), cancellable = true) + private void antarchy$routePortalSectionCompile(SectionRenderDispatcher.RenderSection section, CallbackInfo ci) { + if (PortalGunPortalViewAreaManager.routeSectionCompiled(section)) { + ci.cancel(); + } + } + + @Inject(method = "setSectionDirty(IIIZ)V", at = @At("TAIL")) + private void antarchy$dirtyPortalSection(int sectionX, int sectionY, int sectionZ, boolean playerChanged, CallbackInfo ci) { + PortalGunPortalViewAreaManager.onSectionDirty(sectionX, sectionY, sectionZ, playerChanged); + } + + @Inject(method = "onChunkLoaded", at = @At("TAIL")) + private void antarchy$updatePortalChunkGraph(ChunkPos pos, CallbackInfo ci) { + PortalGunPortalViewAreaManager.onChunkLoaded(pos); + } + +} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/client/LivingEntityPortalLerpAccessor.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/client/LivingEntityPortalLerpAccessor.java new file mode 100644 index 00000000..433c9cf5 --- /dev/null +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/client/LivingEntityPortalLerpAccessor.java @@ -0,0 +1,17 @@ +package com.craisinlord.antarchy.mixins.client; + +import net.minecraft.world.entity.LivingEntity; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Mutable; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(LivingEntity.class) +public interface LivingEntityPortalLerpAccessor { + @Mutable + @Accessor("lerpSteps") + void antarchy$setLerpSteps(int steps); + + @Mutable + @Accessor("lerpHeadSteps") + void antarchy$setLerpHeadSteps(int steps); +} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/client/MinecraftMainRenderTargetAccessor.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/client/MinecraftMainRenderTargetAccessor.java new file mode 100644 index 00000000..0fd7d763 --- /dev/null +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/client/MinecraftMainRenderTargetAccessor.java @@ -0,0 +1,14 @@ +package com.craisinlord.antarchy.mixins.client; + +import com.mojang.blaze3d.pipeline.RenderTarget; +import net.minecraft.client.Minecraft; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Mutable; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(Minecraft.class) +public interface MinecraftMainRenderTargetAccessor { + @Mutable + @Accessor("mainRenderTarget") + void antarchy$setMainRenderTarget(RenderTarget renderTarget); +} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/client/PortalGunEntityTeleportPacketMixin.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/client/PortalGunEntityTeleportPacketMixin.java new file mode 100644 index 00000000..4a96a9f9 --- /dev/null +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/client/PortalGunEntityTeleportPacketMixin.java @@ -0,0 +1,68 @@ +package com.craisinlord.antarchy.mixins.client; + +import com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity; +import com.craisinlord.antarchy.content.portalgun.PortalGunWorldPortalShape; +import net.minecraft.client.Minecraft; +import net.minecraft.client.multiplayer.ClientPacketListener; +import net.minecraft.network.protocol.game.ClientboundTeleportEntityPacket; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.phys.AABB; +import net.minecraft.world.phys.Vec3; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(ClientPacketListener.class) +public abstract class PortalGunEntityTeleportPacketMixin { + @Inject(method = "handleTeleportEntity", at = @At("TAIL")) + private void antarchy$snapPortalTransitEntity(ClientboundTeleportEntityPacket packet, CallbackInfo ci) { + Minecraft minecraft = Minecraft.getInstance(); + if (minecraft.level == null) { + return; + } + Entity entity = minecraft.level.getEntity(packet.getId()); + Vec3 destination = new Vec3(packet.getX(), packet.getY(), packet.getZ()); + if (entity == null) { + return; + } + antarchy$snapToPortalExit(entity, destination); + } + + private static void antarchy$snapToPortalExit(Entity entity, Vec3 destination) { + if (!isNearPortalExit(entity, destination)) { + return; + } + entity.lerpTo(destination.x, destination.y, destination.z, entity.getYRot(), entity.getXRot(), 0); + entity.setPortalCooldown(PortalGunPortalEntity.TELEPORT_COOLDOWN_TICKS); + entity.setPos(destination.x, destination.y, destination.z); + entity.xo = destination.x; + entity.yo = destination.y; + entity.zo = destination.z; + entity.yRotO = entity.getYRot(); + entity.xRotO = entity.getXRot(); + if (entity instanceof LivingEntity livingEntity) { + LivingEntityPortalLerpAccessor accessor = (LivingEntityPortalLerpAccessor) livingEntity; + accessor.antarchy$setLerpHeadSteps(0); + } + } + + private static boolean isNearPortalExit(Entity entity, Vec3 destination) { + Vec3 offset = destination.subtract(entity.position()); + double entityExtent = Math.max(entity.getBbWidth(), entity.getBbHeight()); + double edgeAllowance = entityExtent * 0.5D; + AABB search = entity.getBoundingBox().move(offset).inflate(entityExtent + PortalGunWorldPortalShape.DEFAULT_SCAN_DISTANCE); + Vec3 probe = destination.add(0.0D, entity.getEyeHeight(), 0.0D); + for (PortalGunPortalEntity portal : entity.level().getEntitiesOfClass(PortalGunPortalEntity.class, search, Entity::isAlive)) { + PortalGunWorldPortalShape.PortalLocalCoords coords = portal.getWorldPortalShape().localCoords(probe); + if (coords.depth() >= -entityExtent - 0.5D + && coords.depth() <= PortalGunWorldPortalShape.DEFAULT_SCAN_DISTANCE + 0.5D + && Math.abs(coords.horizontal()) <= portal.getWorldPortalShape().halfWidth() + edgeAllowance + && Math.abs(coords.vertical()) <= portal.getWorldPortalShape().halfHeight() + edgeAllowance) { + return true; + } + } + return false; + } +} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/client/PortalGunZoomFovMixin.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/client/PortalGunZoomFovMixin.java new file mode 100644 index 00000000..b3b6b4b2 --- /dev/null +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/client/PortalGunZoomFovMixin.java @@ -0,0 +1,18 @@ +package com.craisinlord.antarchy.mixins.client; + +import com.craisinlord.antarchy.content.client.PortalGunZoomClientState; +import net.minecraft.client.Camera; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.GameRenderer; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +@Mixin(GameRenderer.class) +public abstract class PortalGunZoomFovMixin { + @Inject(method = "getFov", at = @At("RETURN"), cancellable = true) + private void antarchy$applyPortalGunZoom(Camera camera, float partialTick, boolean useFovSetting, CallbackInfoReturnable cir) { + cir.setReturnValue(PortalGunZoomClientState.applyFov(cir.getReturnValue(), partialTick, Minecraft.getInstance().options)); + } +} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/client/PortalViewChunkCacheMixin.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/client/PortalViewChunkCacheMixin.java new file mode 100644 index 00000000..a75caffa --- /dev/null +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/client/PortalViewChunkCacheMixin.java @@ -0,0 +1,17 @@ +package com.craisinlord.antarchy.mixins.client; + +import com.craisinlord.antarchy.content.client.renderer.PortalGunPortalViewAreaManager; +import net.minecraft.client.multiplayer.ClientChunkCache; +import net.minecraft.world.level.ChunkPos; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(ClientChunkCache.class) +public abstract class PortalViewChunkCacheMixin { + @Inject(method = "drop", at = @At("TAIL")) + private void antarchy$invalidatePortalChunk(ChunkPos pos, CallbackInfo ci) { + PortalGunPortalViewAreaManager.onChunkDropped(pos); + } +} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/client/PortalViewSectionSortingMixin.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/client/PortalViewSectionSortingMixin.java new file mode 100644 index 00000000..6c808236 --- /dev/null +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/client/PortalViewSectionSortingMixin.java @@ -0,0 +1,42 @@ +package com.craisinlord.antarchy.mixins.client; + +import com.craisinlord.antarchy.content.client.renderer.PortalGunPortalViewAreaManager; +import com.craisinlord.antarchy.content.client.renderer.PortalViewSectionMarker; +import com.llamalad7.mixinextras.injector.ModifyExpressionValue; +import org.spongepowered.asm.mixin.Unique; +import java.util.Collection; +import net.minecraft.client.renderer.LevelRenderer; +import net.minecraft.client.renderer.chunk.SectionRenderDispatcher; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.phys.Vec3; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +@Mixin(SectionRenderDispatcher.RenderSection.class) +public abstract class PortalViewSectionSortingMixin implements PortalViewSectionMarker { + @Unique + private boolean antarchy$portalViewSection; + + @Override + public void antarchy$markPortalViewSection() { + this.antarchy$portalViewSection = true; + } + + @Override + public boolean antarchy$isPortalViewSection() { + return this.antarchy$portalViewSection; + } + + @ModifyExpressionValue(method = "createVertexSorting", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/chunk/SectionRenderDispatcher;getCameraPosition()Lnet/minecraft/world/phys/Vec3;")) + private Vec3 antarchy$sortPortalGeometryFromVirtualCamera(Vec3 original) { + return PortalGunPortalViewAreaManager.cameraForSection((SectionRenderDispatcher.RenderSection) (Object) this, original); + } + + @Redirect(method = "updateGlobalBlockEntities", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/LevelRenderer;updateGlobalBlockEntities(Ljava/util/Collection;Ljava/util/Collection;)V")) + private void antarchy$routePortalGlobalBlockEntities(LevelRenderer renderer, Collection removed, Collection added) { + if (!PortalGunPortalViewAreaManager.updateGlobalBlockEntities((SectionRenderDispatcher.RenderSection) (Object) this, removed, added)) { + renderer.updateGlobalBlockEntities(removed, added); + } + } +} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/client/PostChainTargetAccessor.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/client/PostChainTargetAccessor.java new file mode 100644 index 00000000..a9d56508 --- /dev/null +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/client/PostChainTargetAccessor.java @@ -0,0 +1,17 @@ +package com.craisinlord.antarchy.mixins.client; + +import com.mojang.blaze3d.pipeline.RenderTarget; +import net.minecraft.client.renderer.PostChain; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Mutable; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(PostChain.class) +public interface PostChainTargetAccessor { + @Accessor("screenTarget") + RenderTarget antarchy$getScreenTarget(); + + @Mutable + @Accessor("screenTarget") + void antarchy$setScreenTarget(RenderTarget renderTarget); +} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/client/SodiumWorldRendererMixin.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/client/SodiumWorldRendererMixin.java new file mode 100644 index 00000000..851afae1 --- /dev/null +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/client/SodiumWorldRendererMixin.java @@ -0,0 +1,19 @@ +package com.craisinlord.antarchy.mixins.client; + +import com.craisinlord.antarchy.content.client.renderer.SodiumCompat; +import net.minecraft.client.Camera; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Pseudo; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Coerce; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Pseudo +@Mixin(targets = "net.caffeinemc.mods.sodium.client.render.SodiumWorldRenderer", remap = false) +public abstract class SodiumWorldRendererMixin { + @Inject(method = "setupTerrain", at = @At("HEAD"), remap = false) + private void antarchy$captureTerrainSetup(Camera camera, @Coerce Object viewport, boolean spectator, boolean updateChunks, CallbackInfo ci) { + SodiumCompat.captureTerrainSetup(camera, viewport, spectator, updateChunks); + } +} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/compat/scguns/ScgunsProjectileEntityAccessMixin.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/compat/scguns/ScgunsProjectileEntityAccessMixin.java new file mode 100644 index 00000000..a5c3efd2 --- /dev/null +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/compat/scguns/ScgunsProjectileEntityAccessMixin.java @@ -0,0 +1,29 @@ +package com.craisinlord.antarchy.mixins.compat.scguns; + +import com.craisinlord.antarchy.content.gravity.ScgunsProjectileAccess; +import net.minecraft.world.entity.LivingEntity; +import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Pseudo; +import org.spongepowered.asm.mixin.Shadow; + +/* + * Shadows must live on the class that declares the field, so the subclasses + * reach these through ScgunsProjectileAccess instead. + */ +@Mixin(targets = "top.ribs.scguns.entity.projectile.ProjectileEntity") +@Pseudo +public abstract class ScgunsProjectileEntityAccessMixin implements ScgunsProjectileAccess { + @Shadow(remap = false) @Nullable protected LivingEntity shooter; + @Shadow(remap = false) protected double modifiedGravity; + + @Override + public @Nullable LivingEntity antarchy$getScgunsShooter() { + return this.shooter; + } + + @Override + public double antarchy$getScgunsModifiedGravity() { + return this.modifiedGravity; + } +} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/compat/scguns/ScgunsProjectileEntityGravityMixin.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/compat/scguns/ScgunsProjectileEntityGravityMixin.java index f5e1f051..c4b36b8c 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/compat/scguns/ScgunsProjectileEntityGravityMixin.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/compat/scguns/ScgunsProjectileEntityGravityMixin.java @@ -4,14 +4,13 @@ import com.craisinlord.antarchy.content.gravity.AntarchyGravityApi; import com.craisinlord.antarchy.content.gravity.AntarchyGravityDirection; import com.craisinlord.antarchy.content.gravity.AntarchyGravityRotationUtil; import com.craisinlord.antarchy.content.gravity.AntarchyGravityTransition; +import com.craisinlord.antarchy.content.gravity.ScgunsProjectileAccess; import com.craisinlord.antarchy.content.worldgen.thoraxis.ThoraxisUndersideManager; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.phys.Vec3; -import org.jetbrains.annotations.Nullable; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Pseudo; -import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; @@ -28,18 +27,14 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; }) @Pseudo public abstract class ScgunsProjectileEntityGravityMixin { - @Shadow @Nullable protected LivingEntity shooter; - @Shadow protected double modifiedGravity; - @Shadow public abstract Vec3 getDeltaMovement(); - @Shadow public abstract void setDeltaMovement(Vec3 movement); - @Unique private boolean antarchy$launchDirectionAdjusted; @Inject(method = "tick", at = @At("HEAD")) private void antarchy$prepareScgunsProjectile(CallbackInfo ci) { Entity self = (Entity) (Object) this; + LivingEntity shooter = ((ScgunsProjectileAccess) self).antarchy$getScgunsShooter(); boolean inverted = ThoraxisUndersideManager.isInUnderside(self) - || (this.shooter != null && AntarchyGravityApi.isGravityInverted(this.shooter)); + || (shooter != null && AntarchyGravityApi.isGravityInverted(shooter)); if (!inverted) { if (AntarchyGravityApi.isGravityInverted(self) && !AntarchyGravityApi.isGravityForced(self)) { AntarchyGravityApi.setAirborneGravityDirection( @@ -59,9 +54,9 @@ public abstract class ScgunsProjectileEntityGravityMixin { AntarchyGravityTransition.INSTANT ); if (!this.antarchy$launchDirectionAdjusted && self.tickCount <= 1) { - Vec3 motion = this.getDeltaMovement(); + Vec3 motion = self.getDeltaMovement(); if (motion.lengthSqr() > 1.0E-8D) { - this.setDeltaMovement(AntarchyGravityRotationUtil.vecPlayerToWorld( + self.setDeltaMovement(AntarchyGravityRotationUtil.vecPlayerToWorld( motion, AntarchyGravityDirection.UP )); @@ -73,12 +68,13 @@ public abstract class ScgunsProjectileEntityGravityMixin { @Inject(method = "tick", at = @At("TAIL")) private void antarchy$invertScgunsGravity(CallbackInfo ci) { Entity self = (Entity) (Object) this; - if (!AntarchyGravityApi.isGravityInverted(self) || this.modifiedGravity == 0.0D) { + double modifiedGravity = ((ScgunsProjectileAccess) self).antarchy$getScgunsModifiedGravity(); + if (!AntarchyGravityApi.isGravityInverted(self) || modifiedGravity == 0.0D) { return; } // Scorched Guns already adds modifiedGravity once near tick end. Replace // that downward world-Y acceleration with its upward counterpart. - this.setDeltaMovement(this.getDeltaMovement().add(0.0D, -2.0D * this.modifiedGravity, 0.0D)); + self.setDeltaMovement(self.getDeltaMovement().add(0.0D, -2.0D * modifiedGravity, 0.0D)); } } diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/gravity/EntityProjectileGravityMixin.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/gravity/EntityProjectileGravityMixin.java index ec072ea7..aa39bafd 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/gravity/EntityProjectileGravityMixin.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/gravity/EntityProjectileGravityMixin.java @@ -22,6 +22,9 @@ public abstract class EntityProjectileGravityMixin { if (!(self instanceof Projectile projectile)) { return; } + if (projectile.isNoGravity()) { + return; + } boolean inverted = AntarchyGravityApi.isGravityInverted(projectile); if (!inverted && projectile.getOwner() != null) { diff --git a/common-1.21.1/src/main/resources/antarchy-common.mixins.json b/common-1.21.1/src/main/resources/antarchy-common.mixins.json index 7c694031..fe53214d 100644 --- a/common-1.21.1/src/main/resources/antarchy-common.mixins.json +++ b/common-1.21.1/src/main/resources/antarchy-common.mixins.json @@ -6,6 +6,9 @@ "mixins": [ "AbstractMinecartMixin", "AbstractArrowAccessor", + "PortalGunEntityCollisionMixin", + "PortalGunZombieMixin", + "FallingBlockEntityAccessor", "AccessorFireBlock", "AccessorRangedAttribute", "AmericanFoodConsumeMixin", @@ -96,6 +99,7 @@ "gravity.ThrowableProjectileGravityMixin", "gravity.TrackedEntityAccessor", "compat.biolith.MultiNoiseBiomeSourceNullGuardMixin", + "compat.scguns.ScgunsProjectileEntityAccessMixin", "compat.scguns.ScgunsProjectileEntityGravityMixin", "compat.scguns.ScgunsArrowGravityMixin", "compat.irons_spellbooks.IronsSpellbooksProjectileGravityMixin", @@ -135,6 +139,17 @@ "time.GeckoLibAnimationControllerTimeDilationMixin" ], "client": [ + "client.CameraBasisAccessor", + "client.LevelRendererPortalViewAreaAccessor", + "client.LevelRendererPortalViewAreaMixin", + "client.LevelRendererPortalChunkBudgetMixin", + "client.LevelRendererPortalLifecycleMixin", + "client.PortalViewChunkCacheMixin", + "client.PortalViewSectionSortingMixin", + "client.LevelRendererPortalTargetsAccessor", + "client.LevelRendererPortalSceneInvoker", + "client.MinecraftMainRenderTargetAccessor", + "client.PostChainTargetAccessor", "client.AbstractMinecartRendererMixin", "client.EntityLightProbeMixin", "client.CustomBossBarMixin", @@ -154,6 +169,9 @@ "client.MountedVehicleCameraMixin", "client.ParalyzedMouseTurnMixin", "client.LocalPlayerPortalMixin", + "client.PortalGunEntityTeleportPacketMixin", + "client.PortalGunZoomFovMixin", + "client.LivingEntityPortalLerpAccessor", "client.ScreenEffectRendererPortalMixin", "client.ScreenEffectRendererFireVisualMixin", "client.GlimmeringTranslucencyMixin", diff --git a/common-1.21.1/src/main/resources/antarchy-sodium.mixins.json b/common-1.21.1/src/main/resources/antarchy-sodium.mixins.json new file mode 100644 index 00000000..40552668 --- /dev/null +++ b/common-1.21.1/src/main/resources/antarchy-sodium.mixins.json @@ -0,0 +1,13 @@ +{ + "required": false, + "package": "com.craisinlord.antarchy.mixins", + "plugin": "com.craisinlord.antarchy.mixins.AntarchyMixinPlugin", + "compatibilityLevel": "JAVA_21", + "client": [ + "client.SodiumWorldRendererMixin" + ], + "injectors": { + "defaultRequire": 1 + }, + "minVersion": "0.8" +} diff --git a/common-1.21.1/src/main/resources/assets/antarchy/lang/de_de.json b/common-1.21.1/src/main/resources/assets/antarchy/lang/de_de.json index 68d01114..f59b20eb 100644 --- a/common-1.21.1/src/main/resources/assets/antarchy/lang/de_de.json +++ b/common-1.21.1/src/main/resources/assets/antarchy/lang/de_de.json @@ -411,11 +411,6 @@ "tooltip.antarchy.reverie_bottle.capture": "Rechtsklicke mit einer Glasflasche auf eine Reverie, um sie einzufangen.", "tooltip.antarchy.reverie_bottle.release": "Rechtsklick, um die Reverie freizulassen.", "item.antarchy.gravity_gun": "Gravitationskanone", - "item.antarchy.miners_dream": "Traum des Bergmanns", - "tooltip.antarchy.miners_dream.line1": "Rechtsklick, um eine gewundene Höhle zu graben.", - "tooltip.antarchy.miners_dream.line2": "Lässt wertvolle Blöcke unberührt.", - "message.antarchy.miners_dream_disabled": "Der Traum des Bergmanns ist deaktiviert.", - "message.antarchy.miners_dream_busy": "Dein vorheriger Traum entfaltet sich noch.", "item.antarchy.diamond_minecart": "Die Diamantlore", "item.antarchy.cogwheel_pet": "Zahnrad-Haustier", "item.antarchy.fire_dragon_pet": "Feuerdrachen-Haustier", @@ -967,4 +962,6 @@ "subtitles.antarchy.hoverboard_mount": "Hoverboard startet", "subtitles.antarchy.hoverboard_dismount": "Hoverboard stoppt", "subtitles.antarchy.hoverboard_idle": "Hoverboard summt" +, + "entity.antarchy.portal_gun_portal": "Portalgun-Portal" } diff --git a/common-1.21.1/src/main/resources/assets/antarchy/lang/en_us.json b/common-1.21.1/src/main/resources/assets/antarchy/lang/en_us.json index fda1f7b0..8e6426aa 100644 --- a/common-1.21.1/src/main/resources/assets/antarchy/lang/en_us.json +++ b/common-1.21.1/src/main/resources/assets/antarchy/lang/en_us.json @@ -671,11 +671,6 @@ "tooltip.antarchy.reverie_bottle.release": "Right click to release the Reverie.", "item.antarchy.gravity_gun": "Gravity Gun", "item.antarchy.portal_gun": "Portal Gun", - "item.antarchy.miners_dream": "Miner's Dream", - "tooltip.antarchy.miners_dream.line1": "Use to carve a winding cave.", - "tooltip.antarchy.miners_dream.line2": "Leaves valuable blocks untouched.", - "message.antarchy.miners_dream_disabled": "The Miner's Dream is disabled.", - "message.antarchy.miners_dream_busy": "Your previous dream is still unfolding.", "item.antarchy.diamond_minecart": "The Diamond Minecart", "item.antarchy.cogwheel_pet": "Cogwheel Pet", "item.antarchy.fire_dragon_pet": "Fire Dragon Pet", @@ -844,7 +839,11 @@ "tooltip.antarchy.gravity_gun.blacklist": "Does not affect blacklisted blocks or entities.", "tooltip.antarchy.portal_gun.primary": "Attack: fire a blue portal.", "tooltip.antarchy.portal_gun.secondary": "Use: fire an orange portal.", + "tooltip.antarchy.portal_gun.channel": "Channel: %s", + "tooltip.antarchy.portal_gun.owner": "Owner: %s", "tooltip.antarchy.portal_gun.reset": "Sneak + use: fizzle your active portals.", + "tooltip.antarchy.portal_gun.size": "Portal size: %1$s x %2$s", + "tooltip.antarchy.portal_gun.grab_strength": "Grab strength: %s/4", "tooltip.antarchy.antimetal_inversion": "Inverts entites directly below it", "tooltip.antarchy.diamond_minecart.forward": "Will be implemented later.", "tooltip.antarchy.diamond_minecart.rails": " ", @@ -1247,6 +1246,9 @@ "advancements.antarchy.duct_tape.title": "I sawed this boat in half!", "advancements.antarchy.duct_tape.description": "and fixed it with the power of duct tape!", "key.categories.antarchy": "Antarchy", + "key.antarchy.portal_gun_zoom": "Portal Gun Zoom", + "key.antarchy.portal_gun_grab": "Portal Gun Grab", + "key.antarchy.portal_gun_reset": "Portal Gun Reset", "key.antarchy.brutalfly_flap": "Brutalfly Elytra: Flap", "advancements.antarchy.shrink_ray_craft.title": "Honey, I Shrunk the Mobs", "advancements.antarchy.shrink_ray_craft.description": "Craft a Shrink Ray.", @@ -1596,6 +1598,7 @@ ,"guide.antarchy.entry.brutalfly.description": "An oversized Elythian butterfly. Unlike its smaller counterparts, it is incredibly aggressive once out of its cocoon." ,"guide.antarchy.entry.brutalfly.description2": "Like any butterfly, a Brutalfly begins in a cocoon. It is relatively safe to be near the cocoon as long as it remains undamaged." ,"guide.antarchy.entry.brutalfly.description3": "If the Brutalfly is on the assault, all you can do is dispose of it." + ,"guide.antarchy.entry.brutalfly.description4": "Brutaflies are found in Elythia's wooded areas. Look for their cocoons hanging beneath the canopy." ,"guide.antarchy.entry.easter_bunny.title": "Easter Bunny" ,"guide.antarchy.entry.easter_bunny.subtitle": "Entity record" ,"guide.antarchy.entry.easter_bunny.description": "The Easter Bunny lays spawn eggs for all sorts of creatures! Life is like an easter egg, you never know what you're gonna get." @@ -1639,4 +1642,6 @@ ,"subtitles.antarchy.hoverboard_mount": "Hoverboard starts" ,"subtitles.antarchy.hoverboard_dismount": "Hoverboard stops" ,"subtitles.antarchy.hoverboard_idle": "Hoverboard hums" +, + "entity.antarchy.portal_gun_portal": "Portal Gun Portal" } diff --git a/common-1.21.1/src/main/resources/assets/antarchy/lang/es_es.json b/common-1.21.1/src/main/resources/assets/antarchy/lang/es_es.json index dd76c4b6..3567985f 100644 --- a/common-1.21.1/src/main/resources/assets/antarchy/lang/es_es.json +++ b/common-1.21.1/src/main/resources/assets/antarchy/lang/es_es.json @@ -411,11 +411,6 @@ "tooltip.antarchy.reverie_bottle.capture": "Haz clic derecho en una Reverie con una botella de cristal para guardarla.", "tooltip.antarchy.reverie_bottle.release": "Clic derecho para liberar a la Reverie.", "item.antarchy.gravity_gun": "Pistola gravitatoria", - "item.antarchy.miners_dream": "Sueño del minero", - "tooltip.antarchy.miners_dream.line1": "Clic derecho para excavar una cueva sinuosa.", - "tooltip.antarchy.miners_dream.line2": "Deja intactos los bloques valiosos.", - "message.antarchy.miners_dream_disabled": "El Sueño del minero está desactivado.", - "message.antarchy.miners_dream_busy": "Tu sueño anterior todavía se está desarrollando.", "item.antarchy.diamond_minecart": "La vagoneta de diamante", "item.antarchy.cogwheel_pet": "Mascota engranaje", "item.antarchy.fire_dragon_pet": "Mascota de dragón de fuego", @@ -964,4 +959,6 @@ "advancements.antarchy.beetle_buddies.description": "Domestica un escarabajo hércules.", "advancements.antarchy.herculean_task.title": "Tarea hercúlea", "advancements.antarchy.herculean_task.description": "Abate a un escarabajo hércules." +, + "entity.antarchy.portal_gun_portal": "Portal del arma de portales" } diff --git a/common-1.21.1/src/main/resources/assets/antarchy/lang/fr_fr.json b/common-1.21.1/src/main/resources/assets/antarchy/lang/fr_fr.json index 1bb5b8ef..1da3b124 100644 --- a/common-1.21.1/src/main/resources/assets/antarchy/lang/fr_fr.json +++ b/common-1.21.1/src/main/resources/assets/antarchy/lang/fr_fr.json @@ -411,11 +411,6 @@ "tooltip.antarchy.reverie_bottle.capture": "Faites un clic droit sur une Reverie avec une fiole en verre pour la capturer.", "tooltip.antarchy.reverie_bottle.release": "Clic droit pour libérer la Reverie.", "item.antarchy.gravity_gun": "Canon à gravité", - "item.antarchy.miners_dream": "Rêve du mineur", - "tooltip.antarchy.miners_dream.line1": "Clic droit pour creuser une grotte sinueuse.", - "tooltip.antarchy.miners_dream.line2": "Laisse les blocs précieux intacts.", - "message.antarchy.miners_dream_disabled": "Le Rêve du mineur est désactivé.", - "message.antarchy.miners_dream_busy": "Votre rêve précédent est encore en train de se déployer.", "item.antarchy.diamond_minecart": "Le wagonnet de diamant", "item.antarchy.cogwheel_pet": "Familier engrenage", "item.antarchy.fire_dragon_pet": "Familier dragon de feu", @@ -964,4 +959,6 @@ "advancements.antarchy.beetle_buddies.description": "Apprivoisez un scarabée Hercule.", "advancements.antarchy.herculean_task.title": "Travail herculéen", "advancements.antarchy.herculean_task.description": "Terrassez un scarabée Hercule." +, + "entity.antarchy.portal_gun_portal": "Portail du pistolet à portails" } diff --git a/common-1.21.1/src/main/resources/assets/antarchy/lang/it_it.json b/common-1.21.1/src/main/resources/assets/antarchy/lang/it_it.json index e5ceab2e..e3497a4c 100644 --- a/common-1.21.1/src/main/resources/assets/antarchy/lang/it_it.json +++ b/common-1.21.1/src/main/resources/assets/antarchy/lang/it_it.json @@ -411,11 +411,6 @@ "tooltip.antarchy.reverie_bottle.capture": "Fai clic destro su una Reverie con una bottiglia di vetro per catturarla.", "tooltip.antarchy.reverie_bottle.release": "Fai clic destro per liberare la Reverie.", "item.antarchy.gravity_gun": "Cannone gravitazionale", - "item.antarchy.miners_dream": "Sogno del minatore", - "tooltip.antarchy.miners_dream.line1": "Fai clic destro per scavare una grotta tortuosa.", - "tooltip.antarchy.miners_dream.line2": "Lascia intatti i blocchi preziosi.", - "message.antarchy.miners_dream_disabled": "Il Sogno del minatore è disattivato.", - "message.antarchy.miners_dream_busy": "Il tuo sogno precedente si sta ancora dispiegando.", "item.antarchy.diamond_minecart": "Il carrello di diamante", "item.antarchy.cogwheel_pet": "Famiglio ingranaggio", "item.antarchy.fire_dragon_pet": "Famiglio drago di fuoco", @@ -964,4 +959,6 @@ "advancements.antarchy.beetle_buddies.description": "Addomestica uno Scarabeo Ercole.", "advancements.antarchy.herculean_task.title": "Impresa erculea", "advancements.antarchy.herculean_task.description": "Uccidi uno Scarabeo Ercole." +, + "entity.antarchy.portal_gun_portal": "Portale della Portal Gun" } diff --git a/common-1.21.1/src/main/resources/assets/antarchy/lang/ja_jp.json b/common-1.21.1/src/main/resources/assets/antarchy/lang/ja_jp.json index 664b7767..6d81f97f 100644 --- a/common-1.21.1/src/main/resources/assets/antarchy/lang/ja_jp.json +++ b/common-1.21.1/src/main/resources/assets/antarchy/lang/ja_jp.json @@ -411,11 +411,6 @@ "tooltip.antarchy.reverie_bottle.capture": "ガラス瓶を持ってレヴェリーを右クリックすると収納できます。", "tooltip.antarchy.reverie_bottle.release": "右クリックでレヴェリーを解放します。", "item.antarchy.gravity_gun": "重力銃", - "item.antarchy.miners_dream": "採掘者の夢", - "tooltip.antarchy.miners_dream.line1": "右クリックで曲がりくねった洞窟を掘ります。", - "tooltip.antarchy.miners_dream.line2": "貴重なブロックはそのまま残します。", - "message.antarchy.miners_dream_disabled": "採掘者の夢は無効になっています。", - "message.antarchy.miners_dream_busy": "前の夢はまだ展開中です。", "item.antarchy.diamond_minecart": "ダイヤモンドのトロッコ", "item.antarchy.cogwheel_pet": "歯車ペット", "item.antarchy.fire_dragon_pet": "ファイアドラゴンのペット", @@ -964,4 +959,6 @@ "advancements.antarchy.beetle_buddies.description": "ヘラクレスオオカブトを手懐ける。", "advancements.antarchy.herculean_task.title": "ヘラクレス級の難題", "advancements.antarchy.herculean_task.description": "ヘラクレスオオカブトを討伐する。" +, + "entity.antarchy.portal_gun_portal": "ポータルガンのポータル" } diff --git a/common-1.21.1/src/main/resources/assets/antarchy/lang/ko_kr.json b/common-1.21.1/src/main/resources/assets/antarchy/lang/ko_kr.json index 4d74cf03..3a9f171b 100644 --- a/common-1.21.1/src/main/resources/assets/antarchy/lang/ko_kr.json +++ b/common-1.21.1/src/main/resources/assets/antarchy/lang/ko_kr.json @@ -411,11 +411,6 @@ "tooltip.antarchy.reverie_bottle.capture": "유리병으로 레버리를 우클릭하여 담습니다.", "tooltip.antarchy.reverie_bottle.release": "우클릭하여 레버리를 풀어줍니다.", "item.antarchy.gravity_gun": "중력총", - "item.antarchy.miners_dream": "광부의 꿈", - "tooltip.antarchy.miners_dream.line1": "우클릭하여 구불구불한 동굴을 뚫습니다.", - "tooltip.antarchy.miners_dream.line2": "귀중한 블록은 건드리지 않습니다.", - "message.antarchy.miners_dream_disabled": "광부의 꿈이 비활성화되어 있습니다.", - "message.antarchy.miners_dream_busy": "이전 꿈이 아직 펼쳐지고 있습니다.", "item.antarchy.diamond_minecart": "다이아몬드 광산 수레", "item.antarchy.cogwheel_pet": "톱니바퀴 펫", "item.antarchy.fire_dragon_pet": "화염 드래곤 펫", @@ -964,4 +959,6 @@ "advancements.antarchy.beetle_buddies.description": "헤라클레스장수풍뎅이를 길들이세요.", "advancements.antarchy.herculean_task.title": "헤라클레스의 과업", "advancements.antarchy.herculean_task.description": "헤라클레스장수풍뎅이를 처치하세요." +, + "entity.antarchy.portal_gun_portal": "포털 건 포털" } diff --git a/common-1.21.1/src/main/resources/assets/antarchy/lang/pt_br.json b/common-1.21.1/src/main/resources/assets/antarchy/lang/pt_br.json index 55cf70c9..d418f422 100644 --- a/common-1.21.1/src/main/resources/assets/antarchy/lang/pt_br.json +++ b/common-1.21.1/src/main/resources/assets/antarchy/lang/pt_br.json @@ -411,11 +411,6 @@ "tooltip.antarchy.reverie_bottle.capture": "Clique com o botão direito em uma Reverie com um frasco de vidro para guardá-la.", "tooltip.antarchy.reverie_bottle.release": "Clique com o botão direito para soltar a Reverie.", "item.antarchy.gravity_gun": "Arma Gravitacional", - "item.antarchy.miners_dream": "Sonho do Minerador", - "tooltip.antarchy.miners_dream.line1": "Clique com o botão direito para escavar uma caverna sinuosa.", - "tooltip.antarchy.miners_dream.line2": "Deixa blocos valiosos intactos.", - "message.antarchy.miners_dream_disabled": "O Sonho do Minerador está desativado.", - "message.antarchy.miners_dream_busy": "Seu sonho anterior ainda está se desenrolando.", "item.antarchy.diamond_minecart": "O Carrinho de Diamante", "item.antarchy.cogwheel_pet": "Mascote de Engrenagem", "item.antarchy.fire_dragon_pet": "Mascote de Dragão de Fogo", @@ -964,4 +959,6 @@ "advancements.antarchy.beetle_buddies.description": "Domestique um besouro-hércules.", "advancements.antarchy.herculean_task.title": "Tarefa Hercúlea", "advancements.antarchy.herculean_task.description": "Abata um besouro-hércules." +, + "entity.antarchy.portal_gun_portal": "Portal da arma de portais" } diff --git a/common-1.21.1/src/main/resources/assets/antarchy/lang/ru_ru.json b/common-1.21.1/src/main/resources/assets/antarchy/lang/ru_ru.json index 1bc3167c..e05f5a01 100644 --- a/common-1.21.1/src/main/resources/assets/antarchy/lang/ru_ru.json +++ b/common-1.21.1/src/main/resources/assets/antarchy/lang/ru_ru.json @@ -387,11 +387,6 @@ "tooltip.antarchy.reverie_bottle.capture": "Нажмите ПКМ стеклянной бутылочкой по грёзе, чтобы поймать её.", "tooltip.antarchy.reverie_bottle.release": "Нажмите ПКМ, чтобы выпустить грёзу.", "item.antarchy.gravity_gun": "Гравитационная пушка", - "item.antarchy.miners_dream": "Мечта шахтёра", - "tooltip.antarchy.miners_dream.line1": "Нажмите ПКМ, чтобы вырезать извилистую пещеру.", - "tooltip.antarchy.miners_dream.line2": "Оставляет ценные блоки нетронутыми.", - "message.antarchy.miners_dream_disabled": "Мечта шахтёра отключена.", - "message.antarchy.miners_dream_busy": "Ваша предыдущая мечта всё ещё воплощается.", "item.antarchy.diamond_minecart": "Алмазная вагонетка", "item.antarchy.cogwheel_pet": "Шестерёнка-питомец", "item.antarchy.fire_dragon_pet": "Огненный дракон-питомец", @@ -842,4 +837,6 @@ "advancements.antarchy.squidzooka_craft.description": "Создайте сквидзуку.", "advancements.antarchy.gravity_gun_craft.title": "Мастер притяжения", "advancements.antarchy.gravity_gun_craft.description": "Создайте гравитационную пушку." +, + "entity.antarchy.portal_gun_portal": "Портал портальной пушки" } diff --git a/common-1.21.1/src/main/resources/assets/antarchy/lang/zh_cn.json b/common-1.21.1/src/main/resources/assets/antarchy/lang/zh_cn.json index bde52c13..7c4c5d58 100644 --- a/common-1.21.1/src/main/resources/assets/antarchy/lang/zh_cn.json +++ b/common-1.21.1/src/main/resources/assets/antarchy/lang/zh_cn.json @@ -411,11 +411,6 @@ "tooltip.antarchy.reverie_bottle.capture": "手持玻璃瓶右键雷弗里即可将其收纳。", "tooltip.antarchy.reverie_bottle.release": "右键释放雷弗里。", "item.antarchy.gravity_gun": "重力枪", - "item.antarchy.miners_dream": "矿工之梦", - "tooltip.antarchy.miners_dream.line1": "右键挖出一条蜿蜒洞穴。", - "tooltip.antarchy.miners_dream.line2": "不会破坏贵重方块。", - "message.antarchy.miners_dream_disabled": "矿工之梦已禁用。", - "message.antarchy.miners_dream_busy": "你之前的梦仍在展开。", "item.antarchy.diamond_minecart": "钻石矿车", "item.antarchy.cogwheel_pet": "齿轮宠物", "item.antarchy.fire_dragon_pet": "火龙宠物", @@ -964,4 +959,6 @@ "advancements.antarchy.beetle_buddies.description": "驯服一只赫拉克勒斯甲虫。", "advancements.antarchy.herculean_task.title": "赫拉克勒斯之业", "advancements.antarchy.herculean_task.description": "斩杀一只赫拉克勒斯甲虫。" +, + "entity.antarchy.portal_gun_portal": "传送门枪传送门" } diff --git a/common-1.21.1/src/main/resources/assets/antarchy/lang/zh_tw.json b/common-1.21.1/src/main/resources/assets/antarchy/lang/zh_tw.json index 2c3a6435..88424f7c 100644 --- a/common-1.21.1/src/main/resources/assets/antarchy/lang/zh_tw.json +++ b/common-1.21.1/src/main/resources/assets/antarchy/lang/zh_tw.json @@ -411,11 +411,6 @@ "tooltip.antarchy.reverie_bottle.capture": "手持玻璃瓶右鍵雷弗裡即可將其收納。", "tooltip.antarchy.reverie_bottle.release": "右鍵釋放雷弗裡。", "item.antarchy.gravity_gun": "重力槍", - "item.antarchy.miners_dream": "礦工之夢", - "tooltip.antarchy.miners_dream.line1": "右鍵挖出一條蜿蜒洞穴。", - "tooltip.antarchy.miners_dream.line2": "不會破壞貴重方塊。", - "message.antarchy.miners_dream_disabled": "礦工之夢已禁用。", - "message.antarchy.miners_dream_busy": "你之前的夢仍在展開。", "item.antarchy.diamond_minecart": "鑽石礦車", "item.antarchy.cogwheel_pet": "齒輪寵物", "item.antarchy.fire_dragon_pet": "火龍寵物", @@ -964,4 +959,6 @@ "advancements.antarchy.beetle_buddies.description": "馴服一隻赫拉克勒斯甲蟲。", "advancements.antarchy.herculean_task.title": "赫拉克勒斯之業", "advancements.antarchy.herculean_task.description": "斬殺一隻赫拉克勒斯甲蟲。" +, + "entity.antarchy.portal_gun_portal": "傳送門槍傳送門" } diff --git a/common-1.21.1/src/main/resources/assets/antarchy/licenses/aperture_innovations_mit.txt b/common-1.21.1/src/main/resources/assets/antarchy/licenses/aperture_innovations_mit.txt new file mode 100644 index 00000000..bf7da1e1 --- /dev/null +++ b/common-1.21.1/src/main/resources/assets/antarchy/licenses/aperture_innovations_mit.txt @@ -0,0 +1,25 @@ +Crosshair texture, portal vortex textures, and portal audio adapted from Aperture Innovations by MisterSecret312. +Source: https://github.com/mistersecret312/ApertureInnovations +The Aperture Innovations mod metadata declares the MIT License. + +MIT License + +Copyright (c) MisterSecret312 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/common-1.21.1/src/main/resources/assets/antarchy/models/item/miners_dream.json b/common-1.21.1/src/main/resources/assets/antarchy/models/item/miners_dream.json deleted file mode 100644 index c7a0f166..00000000 --- a/common-1.21.1/src/main/resources/assets/antarchy/models/item/miners_dream.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "minecraft:item/generated", - "textures": { - "layer0": "antarchy:item/miners_dream" - } -} diff --git a/common-1.21.1/src/main/resources/assets/antarchy/shaders/core/portal_view.fsh b/common-1.21.1/src/main/resources/assets/antarchy/shaders/core/portal_view.fsh new file mode 100644 index 00000000..79b13029 --- /dev/null +++ b/common-1.21.1/src/main/resources/assets/antarchy/shaders/core/portal_view.fsh @@ -0,0 +1,12 @@ +#version 150 + +uniform sampler2D Sampler0; +uniform vec4 ColorModulator; + +noperspective in vec2 portalUv; +out vec4 fragColor; + +void main() { + vec4 color = texture(Sampler0, portalUv); + fragColor = vec4(color.rgb, 1.0) * ColorModulator; +} diff --git a/common-1.21.1/src/main/resources/assets/antarchy/shaders/core/portal_view.json b/common-1.21.1/src/main/resources/assets/antarchy/shaders/core/portal_view.json new file mode 100644 index 00000000..8ff4de2b --- /dev/null +++ b/common-1.21.1/src/main/resources/assets/antarchy/shaders/core/portal_view.json @@ -0,0 +1,14 @@ +{ + "vertex": "antarchy:portal_view", + "fragment": "antarchy:portal_view", + "samplers": [ + { "name": "Sampler0" } + ], + "attributes": [ + "Position", + "UV0" + ], + "uniforms": [ + { "name": "ColorModulator", "type": "float", "count": 4, "values": [ 1.0, 1.0, 1.0, 1.0 ] } + ] +} diff --git a/common-1.21.1/src/main/resources/assets/antarchy/shaders/core/portal_view.vsh b/common-1.21.1/src/main/resources/assets/antarchy/shaders/core/portal_view.vsh new file mode 100644 index 00000000..b6de02e7 --- /dev/null +++ b/common-1.21.1/src/main/resources/assets/antarchy/shaders/core/portal_view.vsh @@ -0,0 +1,11 @@ +#version 150 + +in vec3 Position; +in vec2 UV0; + +noperspective out vec2 portalUv; + +void main() { + gl_Position = vec4(Position, 1.0); + portalUv = UV0; +} diff --git a/common-1.21.1/src/main/resources/assets/antarchy/sounds.json b/common-1.21.1/src/main/resources/assets/antarchy/sounds.json index f17ed18b..c10b45f8 100644 --- a/common-1.21.1/src/main/resources/assets/antarchy/sounds.json +++ b/common-1.21.1/src/main/resources/assets/antarchy/sounds.json @@ -1738,13 +1738,13 @@ "subtitle": "subtitles.antarchy.portal_gun_fire_blue", "sounds": [ { - "name": "antarchy:item/portalgun/wpn_portal_gun_fire_blue_01" + "name": "antarchy:portal_aperture/portal_gun_fire_primary_1" }, { - "name": "antarchy:item/portalgun/wpn_portal_gun_fire_blue_02" + "name": "antarchy:portal_aperture/portal_gun_fire_primary_2" }, { - "name": "antarchy:item/portalgun/wpn_portal_gun_fire_blue_03" + "name": "antarchy:portal_aperture/portal_gun_fire_primary_3" } ] }, @@ -1752,13 +1752,13 @@ "subtitle": "subtitles.antarchy.portal_gun_fire_orange", "sounds": [ { - "name": "antarchy:item/portalgun/wpn_portal_gun_fire_red_01" + "name": "antarchy:portal_aperture/portal_gun_fire_secondary_1" }, { - "name": "antarchy:item/portalgun/wpn_portal_gun_fire_red_02" + "name": "antarchy:portal_aperture/portal_gun_fire_secondary_2" }, { - "name": "antarchy:item/portalgun/wpn_portal_gun_fire_red_03" + "name": "antarchy:portal_aperture/portal_gun_fire_secondary_3" } ] }, @@ -1766,19 +1766,16 @@ "subtitle": "subtitles.antarchy.portal_gun_invalid_surface", "sounds": [ { - "name": "antarchy:item/portalgun/portal_invalid_surface_swt_01" + "name": "antarchy:portal_aperture/portal_invalid_surface_1" }, { - "name": "antarchy:portal/portal_invalid_surface_01" + "name": "antarchy:portal_aperture/portal_invalid_surface_2" }, { - "name": "antarchy:portal/portal_invalid_surface_02" + "name": "antarchy:portal_aperture/portal_invalid_surface_3" }, { - "name": "antarchy:portal/portal_invalid_surface_03" - }, - { - "name": "antarchy:portal/portal_invalid_surface_04" + "name": "antarchy:portal_aperture/portal_invalid_surface_4" } ] }, @@ -1786,7 +1783,7 @@ "subtitle": "subtitles.antarchy.portal_open_blue", "sounds": [ { - "name": "antarchy:portal/portal_open_blue_01" + "name": "antarchy:portal_aperture/portal_open_primary_1" } ] }, @@ -1794,7 +1791,10 @@ "subtitle": "subtitles.antarchy.portal_open_orange", "sounds": [ { - "name": "antarchy:portal/portal_open_red_01" + "name": "antarchy:portal_aperture/portal_open_secondary_1" + }, + { + "name": "antarchy:portal_aperture/portal_open_secondary_2" } ] }, @@ -1802,10 +1802,10 @@ "subtitle": "subtitles.antarchy.portal_fizzle", "sounds": [ { - "name": "antarchy:portal/portal_fizzle_01" + "name": "antarchy:portal_aperture/portal_fizzle_1" }, { - "name": "antarchy:portal/portal_fizzle_02" + "name": "antarchy:portal_aperture/portal_fizzle_2" } ] }, @@ -1813,13 +1813,13 @@ "subtitle": "subtitles.antarchy.portal_enter", "sounds": [ { - "name": "antarchy:portal/portal_enter_01" + "name": "antarchy:portal_aperture/portal_enter_1" }, { - "name": "antarchy:portal/portal_enter_02" + "name": "antarchy:portal_aperture/portal_enter_2" }, { - "name": "antarchy:portal/portal_enter_03" + "name": "antarchy:portal_aperture/portal_enter_3" } ] }, @@ -1827,10 +1827,13 @@ "subtitle": "subtitles.antarchy.portal_exit", "sounds": [ { - "name": "antarchy:portal/portal_exit_01" + "name": "antarchy:portal_aperture/portal_enter_1" }, { - "name": "antarchy:portal/portal_exit_02" + "name": "antarchy:portal_aperture/portal_enter_2" + }, + { + "name": "antarchy:portal_aperture/portal_enter_3" } ] }, @@ -1838,7 +1841,7 @@ "subtitle": "subtitles.antarchy.portal_ambient", "sounds": [ { - "name": "antarchy:portal/wpn_portal_ambient_lp_01" + "name": "antarchy:portal_aperture/portal_ambient" } ] }, diff --git a/common-1.21.1/src/main/resources/assets/antarchy/sounds/portal_aperture/portal_ambient.ogg b/common-1.21.1/src/main/resources/assets/antarchy/sounds/portal_aperture/portal_ambient.ogg new file mode 100644 index 00000000..fad34aee Binary files /dev/null and b/common-1.21.1/src/main/resources/assets/antarchy/sounds/portal_aperture/portal_ambient.ogg differ diff --git a/common-1.21.1/src/main/resources/assets/antarchy/sounds/portal_aperture/portal_enter_1.ogg b/common-1.21.1/src/main/resources/assets/antarchy/sounds/portal_aperture/portal_enter_1.ogg new file mode 100644 index 00000000..51a32878 Binary files /dev/null and b/common-1.21.1/src/main/resources/assets/antarchy/sounds/portal_aperture/portal_enter_1.ogg differ diff --git a/common-1.21.1/src/main/resources/assets/antarchy/sounds/portal_aperture/portal_enter_2.ogg b/common-1.21.1/src/main/resources/assets/antarchy/sounds/portal_aperture/portal_enter_2.ogg new file mode 100644 index 00000000..41bb433a Binary files /dev/null and b/common-1.21.1/src/main/resources/assets/antarchy/sounds/portal_aperture/portal_enter_2.ogg differ diff --git a/common-1.21.1/src/main/resources/assets/antarchy/sounds/portal_aperture/portal_enter_3.ogg b/common-1.21.1/src/main/resources/assets/antarchy/sounds/portal_aperture/portal_enter_3.ogg new file mode 100644 index 00000000..f84196be Binary files /dev/null and b/common-1.21.1/src/main/resources/assets/antarchy/sounds/portal_aperture/portal_enter_3.ogg differ diff --git a/common-1.21.1/src/main/resources/assets/antarchy/sounds/portal_aperture/portal_fizzle_1.ogg b/common-1.21.1/src/main/resources/assets/antarchy/sounds/portal_aperture/portal_fizzle_1.ogg new file mode 100644 index 00000000..f4d1ccd4 Binary files /dev/null and b/common-1.21.1/src/main/resources/assets/antarchy/sounds/portal_aperture/portal_fizzle_1.ogg differ diff --git a/common-1.21.1/src/main/resources/assets/antarchy/sounds/portal_aperture/portal_fizzle_2.ogg b/common-1.21.1/src/main/resources/assets/antarchy/sounds/portal_aperture/portal_fizzle_2.ogg new file mode 100644 index 00000000..596618ab Binary files /dev/null and b/common-1.21.1/src/main/resources/assets/antarchy/sounds/portal_aperture/portal_fizzle_2.ogg differ diff --git a/common-1.21.1/src/main/resources/assets/antarchy/sounds/portal_aperture/portal_gun_fire_primary_1.ogg b/common-1.21.1/src/main/resources/assets/antarchy/sounds/portal_aperture/portal_gun_fire_primary_1.ogg new file mode 100644 index 00000000..ca20531e Binary files /dev/null and b/common-1.21.1/src/main/resources/assets/antarchy/sounds/portal_aperture/portal_gun_fire_primary_1.ogg differ diff --git a/common-1.21.1/src/main/resources/assets/antarchy/sounds/portal_aperture/portal_gun_fire_primary_2.ogg b/common-1.21.1/src/main/resources/assets/antarchy/sounds/portal_aperture/portal_gun_fire_primary_2.ogg new file mode 100644 index 00000000..afc71c1a Binary files /dev/null and b/common-1.21.1/src/main/resources/assets/antarchy/sounds/portal_aperture/portal_gun_fire_primary_2.ogg differ diff --git a/common-1.21.1/src/main/resources/assets/antarchy/sounds/portal_aperture/portal_gun_fire_primary_3.ogg b/common-1.21.1/src/main/resources/assets/antarchy/sounds/portal_aperture/portal_gun_fire_primary_3.ogg new file mode 100644 index 00000000..3866132b Binary files /dev/null and b/common-1.21.1/src/main/resources/assets/antarchy/sounds/portal_aperture/portal_gun_fire_primary_3.ogg differ diff --git a/common-1.21.1/src/main/resources/assets/antarchy/sounds/portal_aperture/portal_gun_fire_secondary_1.ogg b/common-1.21.1/src/main/resources/assets/antarchy/sounds/portal_aperture/portal_gun_fire_secondary_1.ogg new file mode 100644 index 00000000..d3e7f87d Binary files /dev/null and b/common-1.21.1/src/main/resources/assets/antarchy/sounds/portal_aperture/portal_gun_fire_secondary_1.ogg differ diff --git a/common-1.21.1/src/main/resources/assets/antarchy/sounds/portal_aperture/portal_gun_fire_secondary_2.ogg b/common-1.21.1/src/main/resources/assets/antarchy/sounds/portal_aperture/portal_gun_fire_secondary_2.ogg new file mode 100644 index 00000000..115b191d Binary files /dev/null and b/common-1.21.1/src/main/resources/assets/antarchy/sounds/portal_aperture/portal_gun_fire_secondary_2.ogg differ diff --git a/common-1.21.1/src/main/resources/assets/antarchy/sounds/portal_aperture/portal_gun_fire_secondary_3.ogg b/common-1.21.1/src/main/resources/assets/antarchy/sounds/portal_aperture/portal_gun_fire_secondary_3.ogg new file mode 100644 index 00000000..8bd4cbce Binary files /dev/null and b/common-1.21.1/src/main/resources/assets/antarchy/sounds/portal_aperture/portal_gun_fire_secondary_3.ogg differ diff --git a/common-1.21.1/src/main/resources/assets/antarchy/sounds/portal_aperture/portal_invalid_surface_1.ogg b/common-1.21.1/src/main/resources/assets/antarchy/sounds/portal_aperture/portal_invalid_surface_1.ogg new file mode 100644 index 00000000..21cf038e Binary files /dev/null and b/common-1.21.1/src/main/resources/assets/antarchy/sounds/portal_aperture/portal_invalid_surface_1.ogg differ diff --git a/common-1.21.1/src/main/resources/assets/antarchy/sounds/portal_aperture/portal_invalid_surface_2.ogg b/common-1.21.1/src/main/resources/assets/antarchy/sounds/portal_aperture/portal_invalid_surface_2.ogg new file mode 100644 index 00000000..e35ea762 Binary files /dev/null and b/common-1.21.1/src/main/resources/assets/antarchy/sounds/portal_aperture/portal_invalid_surface_2.ogg differ diff --git a/common-1.21.1/src/main/resources/assets/antarchy/sounds/portal_aperture/portal_invalid_surface_3.ogg b/common-1.21.1/src/main/resources/assets/antarchy/sounds/portal_aperture/portal_invalid_surface_3.ogg new file mode 100644 index 00000000..7c941cfe Binary files /dev/null and b/common-1.21.1/src/main/resources/assets/antarchy/sounds/portal_aperture/portal_invalid_surface_3.ogg differ diff --git a/common-1.21.1/src/main/resources/assets/antarchy/sounds/portal_aperture/portal_invalid_surface_4.ogg b/common-1.21.1/src/main/resources/assets/antarchy/sounds/portal_aperture/portal_invalid_surface_4.ogg new file mode 100644 index 00000000..e54bab20 Binary files /dev/null and b/common-1.21.1/src/main/resources/assets/antarchy/sounds/portal_aperture/portal_invalid_surface_4.ogg differ diff --git a/common-1.21.1/src/main/resources/assets/antarchy/sounds/portal_aperture/portal_open_primary_1.ogg b/common-1.21.1/src/main/resources/assets/antarchy/sounds/portal_aperture/portal_open_primary_1.ogg new file mode 100644 index 00000000..86af3d84 Binary files /dev/null and b/common-1.21.1/src/main/resources/assets/antarchy/sounds/portal_aperture/portal_open_primary_1.ogg differ diff --git a/common-1.21.1/src/main/resources/assets/antarchy/sounds/portal_aperture/portal_open_secondary_1.ogg b/common-1.21.1/src/main/resources/assets/antarchy/sounds/portal_aperture/portal_open_secondary_1.ogg new file mode 100644 index 00000000..5c46e7ce Binary files /dev/null and b/common-1.21.1/src/main/resources/assets/antarchy/sounds/portal_aperture/portal_open_secondary_1.ogg differ diff --git a/common-1.21.1/src/main/resources/assets/antarchy/sounds/portal_aperture/portal_open_secondary_2.ogg b/common-1.21.1/src/main/resources/assets/antarchy/sounds/portal_aperture/portal_open_secondary_2.ogg new file mode 100644 index 00000000..85136fad Binary files /dev/null and b/common-1.21.1/src/main/resources/assets/antarchy/sounds/portal_aperture/portal_open_secondary_2.ogg differ diff --git a/common-1.21.1/src/main/resources/assets/antarchy/textures/entity/portal_gun/portal_blue_vortex.png b/common-1.21.1/src/main/resources/assets/antarchy/textures/entity/portal_gun/portal_blue_vortex.png new file mode 100644 index 00000000..aaef912a Binary files /dev/null and b/common-1.21.1/src/main/resources/assets/antarchy/textures/entity/portal_gun/portal_blue_vortex.png differ diff --git a/common-1.21.1/src/main/resources/assets/antarchy/textures/entity/portal_gun/portal_orange_vortex.png b/common-1.21.1/src/main/resources/assets/antarchy/textures/entity/portal_gun/portal_orange_vortex.png new file mode 100644 index 00000000..6649e933 Binary files /dev/null and b/common-1.21.1/src/main/resources/assets/antarchy/textures/entity/portal_gun/portal_orange_vortex.png differ diff --git a/common-1.21.1/src/main/resources/assets/antarchy/textures/gui/portal_gun_crosshair.png b/common-1.21.1/src/main/resources/assets/antarchy/textures/gui/portal_gun_crosshair.png new file mode 100644 index 00000000..1adf142c Binary files /dev/null and b/common-1.21.1/src/main/resources/assets/antarchy/textures/gui/portal_gun_crosshair.png differ diff --git a/common-1.21.1/src/main/resources/assets/antarchy/textures/item/miners_dream.png b/common-1.21.1/src/main/resources/assets/antarchy/textures/item/miners_dream.png deleted file mode 100644 index d8371b7b..00000000 Binary files a/common-1.21.1/src/main/resources/assets/antarchy/textures/item/miners_dream.png and /dev/null differ diff --git a/common-1.21.1/src/main/resources/data/antarchy/computer/entry/brutalfly.json b/common-1.21.1/src/main/resources/data/antarchy/computer/entry/brutalfly.json index 6fc1ecbf..66756563 100644 --- a/common-1.21.1/src/main/resources/data/antarchy/computer/entry/brutalfly.json +++ b/common-1.21.1/src/main/resources/data/antarchy/computer/entry/brutalfly.json @@ -3,9 +3,11 @@ "category": "elythia", "title": "guide.antarchy.entry.brutalfly.title", "subtitle": "guide.antarchy.entry.brutalfly.subtitle", - "description": ["guide.antarchy.entry.brutalfly.description", "guide.antarchy.entry.brutalfly.description2", "guide.antarchy.entry.brutalfly.description3"], + "description": ["guide.antarchy.entry.brutalfly.description", "guide.antarchy.entry.brutalfly.description2", "guide.antarchy.entry.brutalfly.description3", "guide.antarchy.entry.brutalfly.description4"], "entity": "antarchy:brutalfly", "cover_entity": "antarchy:brutalfly", + "locator": "antarchy:brutalfly", + "dimension": "antarchy:elythia", "rotation": 45, "render_scale": 0.6 } diff --git a/common-1.21.1/src/main/resources/data/antarchy/loot_table/chests/temple_of_technoblade/treasure.json b/common-1.21.1/src/main/resources/data/antarchy/loot_table/chests/temple_of_technoblade/treasure.json index f8b30a15..c1695f46 100644 --- a/common-1.21.1/src/main/resources/data/antarchy/loot_table/chests/temple_of_technoblade/treasure.json +++ b/common-1.21.1/src/main/resources/data/antarchy/loot_table/chests/temple_of_technoblade/treasure.json @@ -127,7 +127,7 @@ { "type": "minecraft:item", "name": "antarchy:blood_gods_blade", - "weight": 1 + "weight": 2 }, { "type": "minecraft:item", diff --git a/common-1.21.1/src/main/resources/data/antarchy/recipe/big_bertha_blade.json b/common-1.21.1/src/main/resources/data/antarchy/recipe/big_bertha_blade.json index 9593727f..13957e4b 100644 --- a/common-1.21.1/src/main/resources/data/antarchy/recipe/big_bertha_blade.json +++ b/common-1.21.1/src/main/resources/data/antarchy/recipe/big_bertha_blade.json @@ -33,4 +33,4 @@ "id": "antarchy:big_bertha_blade", "count": 1 } -} +} \ No newline at end of file diff --git a/common-1.21.1/src/main/resources/data/antarchy/recipe/big_bertha_handle.json b/common-1.21.1/src/main/resources/data/antarchy/recipe/big_bertha_handle.json index aded4e9b..3d16262f 100644 --- a/common-1.21.1/src/main/resources/data/antarchy/recipe/big_bertha_handle.json +++ b/common-1.21.1/src/main/resources/data/antarchy/recipe/big_bertha_handle.json @@ -27,4 +27,4 @@ "id": "antarchy:big_bertha_handle", "count": 1 } -} +} \ No newline at end of file diff --git a/common-1.21.1/src/main/resources/data/antarchy/recipe/big_bertha_hilt.json b/common-1.21.1/src/main/resources/data/antarchy/recipe/big_bertha_hilt.json index eb198b6e..28d51611 100644 --- a/common-1.21.1/src/main/resources/data/antarchy/recipe/big_bertha_hilt.json +++ b/common-1.21.1/src/main/resources/data/antarchy/recipe/big_bertha_hilt.json @@ -36,4 +36,4 @@ "id": "antarchy:big_bertha_hilt", "count": 1 } -} +} \ No newline at end of file diff --git a/common-1.21.1/src/main/resources/data/antarchy/recipe/miners_dream.json.disabled b/common-1.21.1/src/main/resources/data/antarchy/recipe/miners_dream.json.disabled deleted file mode 100644 index ac875084..00000000 --- a/common-1.21.1/src/main/resources/data/antarchy/recipe/miners_dream.json.disabled +++ /dev/null @@ -1,19 +0,0 @@ -{ - "type": "minecraft:crafting_shaped", - "category": "misc", - "pattern": [ - "GGG", - "RDR", - "TTT" - ], - "key": { - "G": { "item": "antarchy:flytrap_goo" }, - "R": { "item": "minecraft:redstone_block" }, - "D": { "item": "minecraft:diamond_pickaxe" }, - "T": { "item": "minecraft:tnt" } - }, - "result": { - "id": "antarchy:miners_dream", - "count": 1 - } -} diff --git a/common-1.21.1/src/main/resources/data/antarchy/tags/block/miners_dream_carvable.json b/common-1.21.1/src/main/resources/data/antarchy/tags/block/miners_dream_carvable.json deleted file mode 100644 index 6f7d26ee..00000000 --- a/common-1.21.1/src/main/resources/data/antarchy/tags/block/miners_dream_carvable.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "replace": false, - "values": [ - "#minecraft:base_stone_overworld", - "#minecraft:base_stone_nether", - "minecraft:stone", - "minecraft:deepslate", - "minecraft:granite", - "minecraft:diorite", - "minecraft:andesite", - "minecraft:tuff", - "minecraft:calcite", - "minecraft:netherrack", - "minecraft:blackstone", - "minecraft:basalt", - "minecraft:smooth_basalt", - "minecraft:end_stone", - "minecraft:dirt", - "minecraft:coarse_dirt", - "minecraft:rooted_dirt", - "minecraft:podzol", - "minecraft:mycelium", - "minecraft:gravel", - "minecraft:sand", - "minecraft:red_sand", - "minecraft:sandstone", - "minecraft:red_sandstone", - "minecraft:clay", - "minecraft:soul_sand", - "minecraft:soul_soil", - "antarchy:nyxite", - "antarchy:pale_nyxite", - "antarchy:dream_sand", - "antarchy:dream_sandstone" - ] -} diff --git a/common-1.21.1/src/main/resources/data/antarchy/tags/item/big_bertha_ingredient.json b/common-1.21.1/src/main/resources/data/antarchy/tags/item/big_bertha_ingredient.json index 935bc10b..a7b62f62 100644 --- a/common-1.21.1/src/main/resources/data/antarchy/tags/item/big_bertha_ingredient.json +++ b/common-1.21.1/src/main/resources/data/antarchy/tags/item/big_bertha_ingredient.json @@ -25,4 +25,4 @@ "antarchy:hercules_horn", "antarchy:flytrap_goo" ] -} +} \ No newline at end of file diff --git a/common-1.21.1/src/main/resources/data/antarchy/weapon_attributes/blood_gods_blade.json b/common-1.21.1/src/main/resources/data/antarchy/weapon_attributes/blood_gods_blade.json new file mode 100644 index 00000000..d9bee8f9 --- /dev/null +++ b/common-1.21.1/src/main/resources/data/antarchy/weapon_attributes/blood_gods_blade.json @@ -0,0 +1,39 @@ +{ + "attributes": { + "attack_range": 3.0, + "category": "sword", + "attacks": [ + { + "hitbox": "HORIZONTAL_PLANE", + "damage_multiplier": 1, + "angle": 120, + "upswing": 0.5, + "animation": "bettercombat:one_handed_slash_horizontal_right", + "swing_sound": { + "id": "bettercombat:sword_slash" + } + }, + { + "hitbox": "HORIZONTAL_PLANE", + "damage_multiplier": 1, + "angle": 120, + "upswing": 0.5, + "animation": "bettercombat:one_handed_slash_horizontal_left", + "swing_sound": { + "id": "bettercombat:sword_slash" + } + }, + { + "hitbox": "FORWARD_BOX", + "damage_multiplier": 1.4, + "angle": 0, + "upswing": 0.5, + "animation": "bettercombat:one_handed_stab", + "swing_sound": { + "id": "bettercombat:sword_slash", + "pitch": 1.2 + } + } + ] + } +} diff --git a/fabric-1.20.1/src/main/java/com/craisinlord/antarchy/fabric/AntarchyConfigModuleFabric.java b/fabric-1.20.1/src/main/java/com/craisinlord/antarchy/fabric/AntarchyConfigModuleFabric.java index df2b329e..112fb35e 100644 --- a/fabric-1.20.1/src/main/java/com/craisinlord/antarchy/fabric/AntarchyConfigModuleFabric.java +++ b/fabric-1.20.1/src/main/java/com/craisinlord/antarchy/fabric/AntarchyConfigModuleFabric.java @@ -211,7 +211,6 @@ public final class AntarchyConfigModuleFabric { || name.equals("invertProjectilesFromInvertedPlayers") || name.startsWith("gravityGun") || name.startsWith("critterCage") - || name.startsWith("minersDream") || name.equals("potentNyxiteInvertedDurationSeconds") || name.equals("corneaEarNightVisionSeconds") || name.startsWith("american") diff --git a/fabric-1.20.1/src/main/java/com/craisinlord/antarchy/fabric/AntarchyFabricEvents.java b/fabric-1.20.1/src/main/java/com/craisinlord/antarchy/fabric/AntarchyFabricEvents.java index 356796b5..53860bf2 100644 --- a/fabric-1.20.1/src/main/java/com/craisinlord/antarchy/fabric/AntarchyFabricEvents.java +++ b/fabric-1.20.1/src/main/java/com/craisinlord/antarchy/fabric/AntarchyFabricEvents.java @@ -23,7 +23,6 @@ import com.craisinlord.antarchy.content.command.GravityCommand; import com.craisinlord.antarchy.content.command.QueenLocateCommand; import com.craisinlord.antarchy.content.time.TimeDilationCommand; import com.craisinlord.antarchy.content.time.TimeDilationManager; -import com.craisinlord.antarchy.content.item.MinersDreamExcavationManager; import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback; import net.fabricmc.fabric.api.entity.event.v1.ServerLivingEntityEvents; import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents; @@ -164,7 +163,6 @@ public final class AntarchyFabricEvents { tickIchorPlayers(level); tickBloodglassRecharge(level); AttitudeAdjusterSlamManager.tick(level); - MinersDreamExcavationManager.tick(level); } TimeDilationManager.tickServer(server); invertedPlayers.retainAll(activeThisTick); diff --git a/fabric-1.20.1/src/main/java/com/craisinlord/antarchy/fabric/registry/AntarchyFabricItems.java b/fabric-1.20.1/src/main/java/com/craisinlord/antarchy/fabric/registry/AntarchyFabricItems.java index 8e926f20..cb234124 100644 --- a/fabric-1.20.1/src/main/java/com/craisinlord/antarchy/fabric/registry/AntarchyFabricItems.java +++ b/fabric-1.20.1/src/main/java/com/craisinlord/antarchy/fabric/registry/AntarchyFabricItems.java @@ -1257,9 +1257,6 @@ public final class AntarchyFabricItems { () -> new GravityGunItem(new Item.Properties().stacksTo(1).durability(512).rarity(Rarity.RARE).fireResistant())); - // Temporarily disabled: keeping the code but not registering the item for now. - // public static final DeferredItem MINERS_DREAM = ITEMS.register("miners_dream", - // () -> new MinersDreamItem(new Item.Properties().stacksTo(1).rarity(Rarity.RARE))); public static final DeferredItem SQUIDZOOKA = ITEMS.register("squidzooka", diff --git a/fabric-1.20.1/src/main/resources/fabric.mod.json b/fabric-1.20.1/src/main/resources/fabric.mod.json index 9c129c96..80f382e2 100644 --- a/fabric-1.20.1/src/main/resources/fabric.mod.json +++ b/fabric-1.20.1/src/main/resources/fabric.mod.json @@ -32,5 +32,8 @@ "minecraft": "~${minecraft_version}", "java": ">=17", "fabric-api": "*" + }, + "breaks": { + "apple_cows": "*" } } diff --git a/fabric-1.21.1/build.gradle b/fabric-1.21.1/build.gradle index 71079725..eb353483 100644 --- a/fabric-1.21.1/build.gradle +++ b/fabric-1.21.1/build.gradle @@ -37,8 +37,12 @@ dependencies { modImplementation "net.fabricmc:fabric-loader:${fabric_loader_version_1_21_1}" modApi "net.fabricmc.fabric-api:fabric-api:${fabric_api_version_1_21_1}" + modLocalRuntime "maven.modrinth:midnightlib:${midnightlib}" modImplementation "software.bernie.geckolib:geckolib-fabric-1.21.1:${geckolib_version}" modImplementation(files("$rootDir/infinite_dimensions_jars/antos-fabric-1.21.1-${antos_version}.jar")) + if (providers.gradleProperty('antarchySodiumCompatTest').orNull == 'true') { + modLocalRuntime(files("$rootDir/infinite_dimensions_jars/sodium-fabric-0.8.13+mc1.21.1.jar")) + } modCompileOnly("curse.maven:intapi-${intapi_curseforge_id}:${intapi_curseforge_file_id_fabric}") modLocalRuntime("curse.maven:intapi-${intapi_curseforge_id}:${intapi_curseforge_file_id_fabric}") modLocalRuntime("dev.architectury:architectury-fabric:${architectury_api_version}") @@ -46,8 +50,8 @@ dependencies { compileOnly "mezz.jei:jei-1.21.1-fabric-api:${jei_version}" modLocalRuntime "mezz.jei:jei-1.21.1-fabric:${jei_version}" + modLocalRuntime(files("$rootDir/infinite_dimensions_jars/infinity-2.7.3+1.21.1-fabric.jar")) compileOnly(files("$rootDir/infinite_dimensions_jars/infinity-2.7.3+1.21.1-neoforge.jar")) - runtimeOnly(files("$rootDir/infinite_dimensions_jars/infinity-2.7.3+1.21.1-fabric.jar")) compileOnly(files("$rootDir/infinite_dimensions_jars/gravity-api-1.0.7-1.20.1.jar")) compileOnly(files("$rootDir/infinite_dimensions_jars/sable-neoforge-1.21.1-2.0.3.jar")) @@ -71,7 +75,7 @@ dependencies { // space) but load the real Fabric jar at runtime — sizeable_foliage's Fabric jar ships // intermediary-mapped, which this compileOnly classpath can't resolve directly. compileOnly(files("$rootDir/sizeable_foliage_jars/sizeable_foliage-neoforge-1.21.1-1.0.0.jar")) - runtimeOnly(files("$rootDir/sizeable_foliage_jars/sizeable_foliage-fabric-1.21.1-1.0.0.jar")) + modLocalRuntime(files("$rootDir/sizeable_foliage_jars/sizeable_foliage-fabric-1.21.1-1.0.0.jar")) annotationProcessor "org.spongepowered:mixin:0.8.7:processor" implementation(annotationProcessor("io.github.llamalad7:mixinextras-common:${mixin_extras_1_21_1}")) @@ -111,10 +115,12 @@ tasks.named('processResources', ProcessResources).configure { // does — inject the refmap key into fabric's own copy of the shared file after copying. tasks.named('processResources', ProcessResources).configure { doLast { - var commonMixinConfig = new File(destinationDir, 'antarchy-common.mixins.json') - var json = new groovy.json.JsonSlurper().parse(commonMixinConfig) - json.put('refmap', 'antarchy.mixins.refmap.json') - commonMixinConfig.text = groovy.json.JsonOutput.prettyPrint(groovy.json.JsonOutput.toJson(json)) + ['antarchy-common.mixins.json', 'antarchy-sodium.mixins.json'].each { configName -> + var mixinConfig = new File(destinationDir, configName) + var json = new groovy.json.JsonSlurper().parse(mixinConfig) + json.put('refmap', 'antarchy.mixins.refmap.json') + mixinConfig.text = groovy.json.JsonOutput.prettyPrint(groovy.json.JsonOutput.toJson(json)) + } } } diff --git a/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/AntarchyFabricEvents.java b/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/AntarchyFabricEvents.java index b5d2ce5f..5c23159e 100644 --- a/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/AntarchyFabricEvents.java +++ b/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/AntarchyFabricEvents.java @@ -18,6 +18,8 @@ import com.craisinlord.antarchy.content.gravity.AntarchyGravityDirection; import com.craisinlord.antarchy.content.gravity.AntarchyGravityTransition; import com.craisinlord.antarchy.content.horde.CavarynHordeManager; import com.craisinlord.antarchy.content.portal.PermanentPortalManager; +import com.craisinlord.antarchy.content.portalgun.PortalGunProjectileEntity; +import com.craisinlord.antarchy.content.portalgun.PortalGunGrabManager; import com.craisinlord.antarchy.content.command.CavarynCommand; import com.craisinlord.antarchy.content.command.CaterpillarCommand; import com.craisinlord.antarchy.content.command.DimensionalTearCommand; @@ -27,7 +29,6 @@ import com.craisinlord.antarchy.content.command.RoyalCommand; import com.craisinlord.antarchy.content.time.TimeDilationCommand; import com.craisinlord.antarchy.content.time.TimeDilationManager; import com.craisinlord.antarchy.content.time.ChronosphereManager; -import com.craisinlord.antarchy.content.item.MinersDreamExcavationManager; import com.craisinlord.antarchy.content.item.WormHookTetherManager; import com.craisinlord.antarchy.content.entity.trades.ComputerScientistTradeManager; import com.craisinlord.antarchy.content.worldgen.thoraxis.ThoraxisUndersideManager; @@ -36,6 +37,7 @@ import net.fabricmc.fabric.api.entity.event.v1.ServerLivingEntityEvents; import net.fabricmc.fabric.api.entity.event.v1.ServerEntityWorldChangeEvents; import net.fabricmc.fabric.api.entity.event.v1.ServerPlayerEvents; import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents; +import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents; import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents; import net.fabricmc.fabric.api.object.builder.v1.trade.TradeOfferHelper; import net.fabricmc.fabric.api.event.player.AttackEntityCallback; @@ -75,6 +77,9 @@ public final class AntarchyFabricEvents { } public static void register() { + ServerLifecycleEvents.SERVER_STOPPING.register(server -> PortalGunProjectileEntity.releaseAllProjectileTickets()); + ServerLifecycleEvents.SERVER_STOPPING.register(PortalGunGrabManager::clear); + ServerLifecycleEvents.SERVER_STOPPING.register(com.craisinlord.antarchy.content.portalgun.PortalGunResetManager::clear); ServerPlayConnectionEvents.JOIN.register((handler, sender, server) -> { ChronosphereManager.refresh(handler.player); TimeDilationManager.resyncPersistentEffects(handler.player); @@ -183,13 +188,16 @@ public final class AntarchyFabricEvents { : net.minecraft.world.InteractionResultHolder.fail(held); }); AttackEntityCallback.EVENT.register((player, world, hand, entity, hitResult) -> { + if (player.getItemInHand(hand).getItem() instanceof com.craisinlord.antarchy.content.item.PortalGunItem) { + return InteractionResult.FAIL; + } if (!world.isClientSide && player.getItemInHand(hand).getItem() instanceof AttitudeAdjusterItem && player.getAttackStrengthScale(0.5F) >= 0.95F) { AttitudeAdjusterSlamManager.markSpecialHit(player); } return InteractionResult.PASS; }); net.fabricmc.fabric.api.event.player.AttackBlockCallback.EVENT.register((player, world, hand, pos, direction) -> { - if (player.getMainHandItem().getItem() instanceof com.craisinlord.antarchy.content.item.PortalGunItem) { + if (player.getItemInHand(hand).getItem() instanceof com.craisinlord.antarchy.content.item.PortalGunItem) { return InteractionResult.FAIL; } return InteractionResult.PASS; @@ -201,6 +209,7 @@ public final class AntarchyFabricEvents { }); ServerTickEvents.END_SERVER_TICK.register(server -> { + PortalGunGrabManager.tick(server); Set activeThisTick = new HashSet<>(); for (ServerLevel level : server.getAllLevels()) { CavarynHordeManager.tick(level); @@ -211,7 +220,6 @@ public final class AntarchyFabricEvents { tickIchorPlayers(level); tickBloodglassRecharge(level); AttitudeAdjusterSlamManager.tick(level); - MinersDreamExcavationManager.tick(level); for (ServerPlayer player : level.players()) { WormHookTetherManager.tick(player); } diff --git a/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/client/AntarchyFabricClientBootstrap.java b/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/client/AntarchyFabricClientBootstrap.java index 77a7f1e8..59ddeac1 100644 --- a/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/client/AntarchyFabricClientBootstrap.java +++ b/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/client/AntarchyFabricClientBootstrap.java @@ -5,6 +5,7 @@ import com.craisinlord.antarchy.fabric.registry.AntarchyFabricItems; import com.craisinlord.antarchy.fabric.registry.AntarchyFabricMisc; import com.craisinlord.antarchy.Antarchy; +import com.craisinlord.antarchy.content.client.PortalGunCrosshairRenderer; import com.craisinlord.antarchy.content.client.CameraShakeClientState; import com.craisinlord.antarchy.content.client.AntarchyArchivePreviews; import com.craisinlord.antarchy.content.client.HerculesBeetleImpactShakeClientState; @@ -28,6 +29,7 @@ import net.fabricmc.fabric.api.client.render.fluid.v1.FluidRenderHandlerRegistry import net.fabricmc.fabric.api.client.particle.v1.ParticleFactoryRegistry; import net.fabricmc.fabric.api.client.rendering.v1.BlockEntityRendererRegistry; import net.fabricmc.fabric.api.client.rendering.v1.ColorProviderRegistry; +import net.fabricmc.fabric.api.client.rendering.v1.CoreShaderRegistrationCallback; import net.fabricmc.fabric.api.client.rendering.v1.EntityModelLayerRegistry; import net.fabricmc.fabric.api.client.rendering.v1.EntityRendererRegistry; import net.fabricmc.fabric.api.client.rendering.v1.HudRenderCallback; @@ -37,6 +39,7 @@ import net.minecraft.client.model.ChestBoatModel; import net.minecraft.client.gui.screens.MenuScreens; import net.minecraft.client.renderer.BiomeColors; import net.minecraft.client.renderer.RenderType; +import com.mojang.blaze3d.vertex.DefaultVertexFormat; import net.minecraft.client.renderer.texture.TextureAtlas; import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider; import net.minecraft.client.renderer.blockentity.CampfireRenderer; @@ -66,6 +69,18 @@ public final class AntarchyFabricClientBootstrap { } public static void register() { + CoreShaderRegistrationCallback.EVENT.register(context -> { + PortalGunPortalViewRenderer.clearPortalViewShader(); + try { + context.register( + ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "portal_view"), + DefaultVertexFormat.POSITION_TEX, + PortalGunPortalViewRenderer::setPortalViewShader + ); + } catch (java.io.IOException | RuntimeException exception) { + Antarchy.LOGGER.error("Failed to register portal view shader", exception); + } + }); AntarchyArchivePreviews.register(); AntarchyKeyBindings.register(); MenuScreens.register(AntarchyFabricMisc.DORRIE_INVENTORY_MENU.get(), com.craisinlord.antarchy.content.client.screen.DorrieInventoryScreen::new); @@ -611,14 +626,15 @@ public final class AntarchyFabricClientBootstrap { DorrieJumpHudRenderer.render(guiGraphics); HerculesBeetleChargeHudRenderer.render(guiGraphics); com.craisinlord.antarchy.content.client.KingJudgmentFlashHudRenderer.render(guiGraphics); + PortalGunCrosshairRenderer.render(guiGraphics); }); - WorldRenderEvents.AFTER_TRANSLUCENT.register(context -> { + WorldRenderEvents.LAST.register(context -> { BloodCrystalKatanaTrailHandler.render(context); ScorpionWhipTetherRenderHandler.render(context); WormHookTetherRenderHandler.render(context); if (context.matrixStack() != null && com.craisinlord.antarchy.content.client.renderer.PortalGunPortalViewRenderer.isEnabled()) { - com.craisinlord.antarchy.content.client.renderer.PortalGunPortalViewRenderer.render(context.camera(), context.matrixStack().last().pose(), context.tickCounter()); + com.craisinlord.antarchy.content.client.renderer.PortalGunPortalViewRenderer.render(context.camera(), context.matrixStack().last().pose(), context.projectionMatrix(), context.tickCounter()); } }); } diff --git a/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/client/AntarchyKeyBindings.java b/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/client/AntarchyKeyBindings.java index a986d1b0..6eb99e73 100644 --- a/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/client/AntarchyKeyBindings.java +++ b/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/client/AntarchyKeyBindings.java @@ -49,6 +49,24 @@ public final class AntarchyKeyBindings { GLFW.GLFW_KEY_B, CATEGORY ); + public static final KeyMapping PORTAL_GUN_ZOOM = new KeyMapping( + "key.antarchy.portal_gun_zoom", + InputConstants.Type.MOUSE, + GLFW.GLFW_MOUSE_BUTTON_MIDDLE, + CATEGORY + ); + public static final KeyMapping PORTAL_GUN_GRAB = new KeyMapping( + "key.antarchy.portal_gun_grab", + InputConstants.Type.KEYSYM, + GLFW.GLFW_KEY_G, + CATEGORY + ); + public static final KeyMapping PORTAL_GUN_RESET = new KeyMapping( + "key.antarchy.portal_gun_reset", + InputConstants.Type.KEYSYM, + GLFW.GLFW_KEY_R, + CATEGORY + ); private AntarchyKeyBindings() {} @@ -59,6 +77,9 @@ public final class AntarchyKeyBindings { KeyBindingHelper.registerKeyBinding(TIGERS_EYE_CAMOUFLAGE); KeyBindingHelper.registerKeyBinding(ROYAL_INVERSION_TOGGLE); KeyBindingHelper.registerKeyBinding(ROYAL_BOUNDARY); + KeyBindingHelper.registerKeyBinding(PORTAL_GUN_ZOOM); + KeyBindingHelper.registerKeyBinding(PORTAL_GUN_GRAB); + KeyBindingHelper.registerKeyBinding(PORTAL_GUN_RESET); } public static boolean isBrutalflyFlapPressed() { @@ -84,4 +105,20 @@ public final class AntarchyKeyBindings { public static boolean consumeRoyalBoundaryPressed() { return Minecraft.getInstance().screen == null && ROYAL_BOUNDARY.consumeClick(); } + + public static boolean consumePortalGunZoomPressed() { + return Minecraft.getInstance().screen == null && PORTAL_GUN_ZOOM.consumeClick(); + } + + public static boolean consumePortalGunGrabPressed() { + return Minecraft.getInstance().screen == null && PORTAL_GUN_GRAB.consumeClick(); + } + + public static boolean consumePortalGunResetPressed() { + return Minecraft.getInstance().screen == null && PORTAL_GUN_RESET.consumeClick(); + } + + public static boolean isPortalGunResetDown() { + return Minecraft.getInstance().screen == null && (PORTAL_GUN_RESET.isDown() || PORTAL_GUN_RESET.consumeClick()); + } } diff --git a/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/client/PortalGunClientHandler.java b/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/client/PortalGunClientHandler.java index 25883904..59be6aba 100644 --- a/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/client/PortalGunClientHandler.java +++ b/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/client/PortalGunClientHandler.java @@ -1,7 +1,9 @@ package com.craisinlord.antarchy.fabric.client; import com.craisinlord.antarchy.content.item.PortalGunItem; +import com.craisinlord.antarchy.content.client.PortalGunZoomClientState; import com.craisinlord.antarchy.content.network.PortalGunPrimaryPayload; +import com.craisinlord.antarchy.content.network.PortalGunGrabPayload; import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking; import net.minecraft.client.Minecraft; @@ -17,19 +19,39 @@ public final class PortalGunClientHandler { } private static void tickMouse(Minecraft mc) { + if (mc.player == null) { + com.craisinlord.antarchy.content.client.PortalGunGrabClientState.clear(); + com.craisinlord.antarchy.content.client.PortalGunResetClientState.clear(); + } + boolean hasPortalGun = mc.player != null && (mc.player.getMainHandItem().getItem() instanceof PortalGunItem || mc.player.getOffhandItem().getItem() instanceof PortalGunItem); + boolean gameplayInput = mc.screen == null; + boolean held = hasPortalGun && gameplayInput; + boolean zoomPressed = AntarchyKeyBindings.consumePortalGunZoomPressed(); + PortalGunZoomClientState.tick(mc, hasPortalGun, zoomPressed && gameplayInput); + boolean resetActive = com.craisinlord.antarchy.content.client.PortalGunResetClientState.tick( + mc, + held, + AntarchyKeyBindings.isPortalGunResetDown(), + ClientPlayNetworking::send + ); + if (held && AntarchyKeyBindings.consumePortalGunGrabPressed()) { + ClientPlayNetworking.send(new PortalGunGrabPayload()); + } if (mc.player == null || mc.level == null || mc.screen != null) { lastAttackDown = false; return; } - if (!(mc.player.getMainHandItem().getItem() instanceof PortalGunItem)) { + boolean offhand = !(mc.player.getMainHandItem().getItem() instanceof PortalGunItem) + && mc.player.getOffhandItem().getItem() instanceof PortalGunItem; + if (!offhand && !(mc.player.getMainHandItem().getItem() instanceof PortalGunItem)) { lastAttackDown = false; return; } boolean attackDown = mc.options.keyAttack.isDown(); - if (attackDown && !lastAttackDown) { - ClientPlayNetworking.send(new PortalGunPrimaryPayload()); + if (attackDown && !lastAttackDown && !resetActive) { + ClientPlayNetworking.send(new PortalGunPrimaryPayload(offhand)); } lastAttackDown = attackDown; } diff --git a/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/network/AntarchyFabricClientNetworking.java b/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/network/AntarchyFabricClientNetworking.java index 47ecf2e3..53f1cba8 100644 --- a/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/network/AntarchyFabricClientNetworking.java +++ b/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/network/AntarchyFabricClientNetworking.java @@ -42,6 +42,11 @@ public final class AntarchyFabricClientNetworking { ClientPlayNetworking.registerGlobalReceiver(AntarchyGameResultPayload.TYPE, (payload, context) -> context.client().execute(() -> com.craisinlord.antarchy.content.client.AntarchyGameClientState.update(payload))); com.craisinlord.antarchy.content.network.AntarchyGameNetworking.setSender(ClientPlayNetworking::send); + com.craisinlord.antarchy.content.client.PortalGunIndicatorClientState.setRequestSender(ClientPlayNetworking::send); + ClientPlayNetworking.registerGlobalReceiver(PortalGunIndicatorPayload.TYPE, (payload, context) -> + context.client().execute(() -> com.craisinlord.antarchy.content.client.PortalGunIndicatorClientState.update(payload))); + ClientPlayNetworking.registerGlobalReceiver(PortalGunGrabStatePayload.TYPE, (payload, context) -> + context.client().execute(() -> com.craisinlord.antarchy.content.client.PortalGunGrabClientState.update(payload.active()))); ClientPlayNetworking.registerGlobalReceiver(GravityStatePayload.TYPE, (payload, context) -> context.client().execute(() -> AntarchyFabricNetworking.handleGravityState(context.player(), payload))); ClientPlayNetworking.registerGlobalReceiver(BloodglassStatePayload.TYPE, (payload, context) -> diff --git a/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/network/AntarchyFabricNetworking.java b/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/network/AntarchyFabricNetworking.java index 051efdca..6291f2c4 100644 --- a/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/network/AntarchyFabricNetworking.java +++ b/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/network/AntarchyFabricNetworking.java @@ -49,6 +49,8 @@ public final class AntarchyFabricNetworking { registerPayloadTypes(); registerServerReceivers(); com.craisinlord.antarchy.content.network.AntarchyGameNetworkHandler.setResultSender(ServerPlayNetworking::send); + com.craisinlord.antarchy.content.portalgun.PortalGunIndicatorSync.setSender(ServerPlayNetworking::send); + com.craisinlord.antarchy.content.portalgun.PortalGunGrabManager.setStateSender(ServerPlayNetworking::send); } public static void bootstrapMultipartCommon() { @@ -73,6 +75,8 @@ public final class AntarchyFabricNetworking { private static void registerPayloadTypes() { PayloadTypeRegistry.playS2C().register(AntarchyGameResultPayload.TYPE, AntarchyGameResultPayload.STREAM_CODEC); + PayloadTypeRegistry.playS2C().register(PortalGunIndicatorPayload.TYPE, PortalGunIndicatorPayload.STREAM_CODEC); + PayloadTypeRegistry.playS2C().register(PortalGunGrabStatePayload.TYPE, PortalGunGrabStatePayload.STREAM_CODEC); PayloadTypeRegistry.playS2C().register(GravityStatePayload.TYPE, GravityStatePayload.STREAM_CODEC); PayloadTypeRegistry.playS2C().register(BloodglassStatePayload.TYPE, BloodglassStatePayload.STREAM_CODEC); PayloadTypeRegistry.playS2C().register(TigerEyeCamouflageStatePayload.TYPE, TigerEyeCamouflageStatePayload.STREAM_CODEC); @@ -88,6 +92,9 @@ public final class AntarchyFabricNetworking { PayloadTypeRegistry.playC2S().register(GravityGunPrimaryPayload.TYPE, GravityGunPrimaryPayload.STREAM_CODEC); PayloadTypeRegistry.playC2S().register(PortalGunPrimaryPayload.TYPE, PortalGunPrimaryPayload.STREAM_CODEC); + PayloadTypeRegistry.playC2S().register(PortalGunIndicatorRequestPayload.TYPE, PortalGunIndicatorRequestPayload.STREAM_CODEC); + PayloadTypeRegistry.playC2S().register(PortalGunGrabPayload.TYPE, PortalGunGrabPayload.STREAM_CODEC); + PayloadTypeRegistry.playC2S().register(PortalGunResetPayload.TYPE, PortalGunResetPayload.STREAM_CODEC); PayloadTypeRegistry.playC2S().register(EyeOfStormPrimaryPayload.TYPE, EyeOfStormPrimaryPayload.STREAM_CODEC); PayloadTypeRegistry.playC2S().register(OpenGiantFryingPanPayload.TYPE, OpenGiantFryingPanPayload.STREAM_CODEC); PayloadTypeRegistry.playC2S().register(RoyalMountActionPayload.TYPE, RoyalMountActionPayload.STREAM_CODEC); @@ -121,6 +128,12 @@ public final class AntarchyFabricNetworking { context.server().execute(() -> handleGravityGunPrimary(context.player(), payload))); ServerPlayNetworking.registerGlobalReceiver(PortalGunPrimaryPayload.TYPE, (payload, context) -> context.server().execute(() -> handlePortalGunPrimary(context.player(), payload))); + ServerPlayNetworking.registerGlobalReceiver(PortalGunIndicatorRequestPayload.TYPE, (payload, context) -> + context.server().execute(() -> com.craisinlord.antarchy.content.portalgun.PortalGunIndicatorSync.handleRequest(context.player(), payload))); + ServerPlayNetworking.registerGlobalReceiver(PortalGunGrabPayload.TYPE, (payload, context) -> + context.server().execute(() -> com.craisinlord.antarchy.content.portalgun.PortalGunGrabManager.toggle(context.player()))); + ServerPlayNetworking.registerGlobalReceiver(PortalGunResetPayload.TYPE, (payload, context) -> + context.server().execute(() -> com.craisinlord.antarchy.content.portalgun.PortalGunResetManager.handleInput(context.player(), payload.action()))); ServerPlayNetworking.registerGlobalReceiver(EyeOfStormPrimaryPayload.TYPE, (payload, context) -> context.server().execute(() -> handleEyeOfStormPrimary(context.player(), payload))); ServerPlayNetworking.registerGlobalReceiver(OpenGiantFryingPanPayload.TYPE, (payload, context) -> @@ -260,10 +273,11 @@ public final class AntarchyFabricNetworking { } private static void handlePortalGunPrimary(ServerPlayer player, PortalGunPrimaryPayload payload) { - if (!(player.getMainHandItem().getItem() instanceof PortalGunItem portalGunItem)) { + ItemStack stack = payload.offhand() ? player.getOffhandItem() : player.getMainHandItem(); + if (!(stack.getItem() instanceof PortalGunItem portalGunItem)) { return; } - portalGunItem.firePrimary(player.serverLevel(), player, player.getMainHandItem()); + portalGunItem.firePrimary(player.serverLevel(), player, stack, payload.offhand()); } private static void handleRoyalMountAction(ServerPlayer player, RoyalMountActionPayload payload) { diff --git a/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/registry/AntarchyFabricSounds.java b/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/registry/AntarchyFabricSounds.java index 9b9ab0bd..d133177e 100644 --- a/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/registry/AntarchyFabricSounds.java +++ b/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/registry/AntarchyFabricSounds.java @@ -344,6 +344,9 @@ public final class AntarchyFabricSounds { public static final DeferredHolder PORTAL_FIZZLE = registerSoundEvent("portal_fizzle"); + public static final DeferredHolder PORTAL_ENTER = registerSoundEvent("portal_enter"); + public static final DeferredHolder PORTAL_EXIT = registerSoundEvent("portal_exit"); + public static final DeferredHolder PORTAL_AMBIENT = registerSoundEvent("portal_ambient"); public static final DeferredHolder BED_BUG_AMBIENT = registerSoundEvent("bed_bug_ambient"); diff --git a/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/registry/AntarchyFabricSpawnPlacements.java b/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/registry/AntarchyFabricSpawnPlacements.java index 30b004b2..ad609970 100644 --- a/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/registry/AntarchyFabricSpawnPlacements.java +++ b/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/registry/AntarchyFabricSpawnPlacements.java @@ -67,7 +67,7 @@ public final class AntarchyFabricSpawnPlacements { SpawnPlacements.register(AntarchyFabricEntities.VORTEX.get(), SpawnPlacementTypes.NO_RESTRICTIONS, Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, com.craisinlord.antarchy.content.entity.vortex.VortexEntity::canSpawn); SpawnPlacements.register(AntarchyFabricEntities.SCORPION.get(), SpawnPlacementTypes.ON_GROUND, Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, ScorpionEntity::canSpawn); SpawnPlacements.register(AntarchyFabricEntities.BASILISK.get(), SpawnPlacementTypes.ON_GROUND, Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, BasiliskEntity::canSpawn); - SpawnPlacements.register(AntarchyFabricEntities.EMPEROR_SCORPION.get(), SpawnPlacementTypes.ON_GROUND, Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, EmperorScorpionEntity::canSpawn); + SpawnPlacements.register(AntarchyFabricEntities.EMPEROR_SCORPION.get(), SpawnPlacementTypes.NO_RESTRICTIONS, Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, EmperorScorpionEntity::canSpawn); SpawnPlacements.register(AntarchyFabricEntities.TORETERROR.get(), SpawnPlacementTypes.ON_GROUND, Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, ToreterrorEntity::canSpawn); SpawnPlacements.register(AntarchyFabricEntities.CRAWLING_BLIGHT.get(), SpawnPlacementTypes.ON_GROUND, Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, com.craisinlord.antarchy.content.entity.CrawlingBlightEntity::canSpawn); SpawnPlacements.register(AntarchyFabricEntities.SKULKING_FRIGHT.get(), SpawnPlacementTypes.ON_GROUND, Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, com.craisinlord.antarchy.content.entity.SkulkingFrightEntity::canSpawn); diff --git a/fabric-1.21.1/src/main/resources/fabric.mod.json b/fabric-1.21.1/src/main/resources/fabric.mod.json index b2fd1d08..93e915c7 100644 --- a/fabric-1.21.1/src/main/resources/fabric.mod.json +++ b/fabric-1.21.1/src/main/resources/fabric.mod.json @@ -28,6 +28,7 @@ }, "mixins": [ "antarchy-common.mixins.json", + "antarchy-sodium.mixins.json", "antarchy-fabric.mixins.json" ], "depends": { @@ -42,7 +43,9 @@ "sizeable_foliage": "*" }, "breaks": { - "infinity": "<2.7.3" + "infinity": "<2.7.3", + "apple_cows": "*", + "hoverboards": "*" }, "suggests": { "modmenu": "*", diff --git a/forge-1.20.1/src/main/java/com/craisinlord/antarchy/forge/AntarchyConfigModuleForge.java b/forge-1.20.1/src/main/java/com/craisinlord/antarchy/forge/AntarchyConfigModuleForge.java index 6d802296..1058d06d 100644 --- a/forge-1.20.1/src/main/java/com/craisinlord/antarchy/forge/AntarchyConfigModuleForge.java +++ b/forge-1.20.1/src/main/java/com/craisinlord/antarchy/forge/AntarchyConfigModuleForge.java @@ -447,11 +447,6 @@ public final class AntarchyConfigModuleForge { AntarchySettings.setCritterCageMaxCapturableWidth(AntarchyToolsConfig.critterCageMaxCapturableWidth()); AntarchySettings.setCritterCageMaxCapturableHeight(AntarchyToolsConfig.critterCageMaxCapturableHeight()); - AntarchySettings.setMinersDreamEnabled(AntarchyToolsConfig.minersDreamEnabled()); - AntarchySettings.setMinersDreamMinimumRange(AntarchyToolsConfig.minersDreamMinimumRange()); - AntarchySettings.setMinersDreamMaximumRange(AntarchyToolsConfig.minersDreamMaximumRange()); - AntarchySettings.setMinersDreamTorchSpacing(AntarchyToolsConfig.minersDreamTorchSpacing()); - AntarchySettings.setMinersDreamBlocksPerTick(AntarchyToolsConfig.minersDreamBlocksPerTick()); AntarchySettings.setDuctTapeRepairPercentPerUse(AntarchyToolsConfig.ductTapeRepairPercentPerUse()); AntarchySettings.setPotentNyxiteInvertedDurationSeconds(AntarchyToolsConfig.potentNyxiteInvertedDurationSeconds()); diff --git a/forge-1.20.1/src/main/java/com/craisinlord/antarchy/forge/AntarchyToolsConfig.java b/forge-1.20.1/src/main/java/com/craisinlord/antarchy/forge/AntarchyToolsConfig.java index 01126de9..da9e84af 100644 --- a/forge-1.20.1/src/main/java/com/craisinlord/antarchy/forge/AntarchyToolsConfig.java +++ b/forge-1.20.1/src/main/java/com/craisinlord/antarchy/forge/AntarchyToolsConfig.java @@ -187,11 +187,6 @@ public final class AntarchyToolsConfig { // Miner's Dream - private static final ForgeConfigSpec.BooleanValue MINERS_DREAM_ENABLED; - private static final ForgeConfigSpec.IntValue MINERS_DREAM_MINIMUM_RANGE; - private static final ForgeConfigSpec.IntValue MINERS_DREAM_MAXIMUM_RANGE; - private static final ForgeConfigSpec.IntValue MINERS_DREAM_TORCH_SPACING; - private static final ForgeConfigSpec.IntValue MINERS_DREAM_BLOCKS_PER_TICK; // Miscellaneous items / consumables @@ -472,16 +467,6 @@ public final class AntarchyToolsConfig { b.pop(); - // Miner's Dream - - b.push("minersDream"); - MINERS_DREAM_ENABLED = b.comment("Master toggle. If false, the item remains craftable but cannot be used.").define("enabled", true); - MINERS_DREAM_MINIMUM_RANGE = b.comment("Minimum cave length in blocks. Clamped to [8, 256].") .defineInRange("minimumRange", 48, 8, 256); - MINERS_DREAM_MAXIMUM_RANGE = b.comment("Maximum cave length in blocks. Clamped to [8, 256].") .defineInRange("maximumRange", 96, 8, 256); - MINERS_DREAM_TORCH_SPACING = b.comment("Approximate blocks between torch placements along the cave.") .defineInRange("torchSpacing", 10, 4, 64); - MINERS_DREAM_BLOCKS_PER_TICK = b.comment("Maximum blocks removed per server tick while excavating.") .defineInRange("blocksPerTick", 150, 1, 2000); - b.pop(); - // Miscellaneous items / consumables @@ -662,11 +647,6 @@ public final class AntarchyToolsConfig { static double critterCageMaxCapturableWidth() { return CRITTER_CAGE_MAX_CAPTURABLE_WIDTH.get(); } static double critterCageMaxCapturableHeight() { return CRITTER_CAGE_MAX_CAPTURABLE_HEIGHT.get(); } - static boolean minersDreamEnabled() { return MINERS_DREAM_ENABLED.get(); } - static int minersDreamMinimumRange() { return MINERS_DREAM_MINIMUM_RANGE.get(); } - static int minersDreamMaximumRange() { return MINERS_DREAM_MAXIMUM_RANGE.get(); } - static int minersDreamTorchSpacing() { return MINERS_DREAM_TORCH_SPACING.get(); } - static int minersDreamBlocksPerTick() { return MINERS_DREAM_BLOCKS_PER_TICK.get(); } static double ductTapeRepairPercentPerUse() { return DUCT_TAPE_REPAIR_PERCENT_PER_USE.get(); } static double potentNyxiteInvertedDurationSeconds() { return POTENT_NYXITE_INVERTED_DURATION_SECONDS.get(); } diff --git a/forge-1.20.1/src/main/java/com/craisinlord/antarchy/forge/registry/AntarchyForgeEvents.java b/forge-1.20.1/src/main/java/com/craisinlord/antarchy/forge/registry/AntarchyForgeEvents.java index 18e6eef4..37b5f587 100644 --- a/forge-1.20.1/src/main/java/com/craisinlord/antarchy/forge/registry/AntarchyForgeEvents.java +++ b/forge-1.20.1/src/main/java/com/craisinlord/antarchy/forge/registry/AntarchyForgeEvents.java @@ -70,7 +70,6 @@ import net.minecraftforge.event.entity.living.LivingEvent; import net.minecraftforge.event.TickEvent; import net.minecraftforge.event.village.VillagerTradesEvent; import net.minecraftforge.event.village.WandererTradesEvent; -import com.craisinlord.antarchy.content.item.MinersDreamExcavationManager; import net.minecraftforge.fluids.FluidInteractionRegistry; import com.craisinlord.antarchy.forge.network.AntarchyForgeNetworkCore; import com.craisinlord.antarchy.content.time.TimeDilationManager; @@ -106,7 +105,7 @@ public final class AntarchyForgeEvents { MinecraftForge.EVENT_BUS.addListener(AntarchyForgeEvents::tickDreadAndIchor); MinecraftForge.EVENT_BUS.addListener(AntarchyForgeEvents::tickScorpionWhips); MinecraftForge.EVENT_BUS.addListener(AntarchyForgeEvents::tickDreamSandLowGravity); - MinecraftForge.EVENT_BUS.addListener(AntarchyForgeEvents::tickMinersDreamExcavations); + MinecraftForge.EVENT_BUS.addListener(AntarchyForgeEvents::tickAttitudeAdjusterSlams); MinecraftForge.EVENT_BUS.addListener(AntarchyForgeEvents::tickTimeDilation); MinecraftForge.EVENT_BUS.addListener(AntarchyForgeEvents::handleAntiwaterDamage); MinecraftForge.EVENT_BUS.addListener(AntarchyForgeEvents::handleAntiwaterFall); @@ -1007,11 +1006,10 @@ public final class AntarchyForgeEvents { } } - static void tickMinersDreamExcavations(TickEvent.ServerTickEvent event) { + static void tickAttitudeAdjusterSlams(TickEvent.ServerTickEvent event) { if (event.phase != TickEvent.Phase.END) return; for (ServerLevel level : event.getServer().getAllLevels()) { AttitudeAdjusterSlamManager.tick(level); - MinersDreamExcavationManager.tick(level); } } diff --git a/forge-1.20.1/src/main/java/com/craisinlord/antarchy/forge/registry/AntarchyForgeItems.java b/forge-1.20.1/src/main/java/com/craisinlord/antarchy/forge/registry/AntarchyForgeItems.java index 2ca9f565..9bcf022b 100644 --- a/forge-1.20.1/src/main/java/com/craisinlord/antarchy/forge/registry/AntarchyForgeItems.java +++ b/forge-1.20.1/src/main/java/com/craisinlord/antarchy/forge/registry/AntarchyForgeItems.java @@ -779,9 +779,6 @@ public final class AntarchyForgeItems { consumer.accept(AntarchyGeoItemExtensions.crossbowHold(this)); } }); - // Temporarily disabled: keeping the code but not registering the item for now. - // public static final RegistryObject MINERS_DREAM = ITEMS.register("miners_dream", - // () -> new MinersDreamItem(new Item.Properties().stacksTo(1).rarity(Rarity.RARE))); public static final RegistryObject SQUIDZOOKA = ITEMS.register("squidzooka", () -> new SquidzookaItem(new Item.Properties().stacksTo(1).durability(384).rarity(Rarity.RARE)) { @Override diff --git a/forge-1.20.1/src/main/resources/META-INF/mods.toml b/forge-1.20.1/src/main/resources/META-INF/mods.toml index 4dba59d6..7f5f4117 100644 --- a/forge-1.20.1/src/main/resources/META-INF/mods.toml +++ b/forge-1.20.1/src/main/resources/META-INF/mods.toml @@ -22,6 +22,15 @@ displayTest="MATCH_VERSION" versionRange="${minecraft_version_range}" ordering="NONE" side="BOTH" +# Antarchy already includes Apple Cows. Forge 1.20.1 has no "incompatible" dependency type, +# so this optional dependency uses a version range no release can satisfy: if the standalone +# Apple Cows mod is installed, Forge refuses to load instead of running both copies. +[[dependencies.${mod_id}]] + modId="apple_cows" + mandatory=false + versionRange="(,0)" + ordering="NONE" + side="BOTH" [[mixins]] config="antarchy-common.mixins.json" diff --git a/gradle.properties b/gradle.properties index 700d0cbc..02846585 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,7 +6,7 @@ org.gradle.configureondemand=true minecraft_version_1_21_1=1.21.1 modid=antarchy mod_id=antarchy -mod_version=2.1.1 +mod_version=2.2.0 maven_group=com.craisinlord mixin_version=0.15.4+mixin.0.8.7 @@ -54,8 +54,8 @@ jei_version=19.27.0.336 intapi_curseforge_id = 817709 -intapi_curseforge_file_id_neoforge = 7921681 -intapi_curseforge_file_id_fabric = 7921674 +intapi_curseforge_file_id_neoforge = 8868053 +intapi_curseforge_file_id_fabric = 8868049 intapi_curseforge_file_id_common = 5178461 antos_version = 1.2.1 diff --git a/neoforge-1.21.1/build.gradle b/neoforge-1.21.1/build.gradle index f9b7acfc..5bbc0fe4 100644 --- a/neoforge-1.21.1/build.gradle +++ b/neoforge-1.21.1/build.gradle @@ -46,6 +46,9 @@ neoForge { } dependencies { + if (providers.gradleProperty('antarchySodiumCompatTest').orNull == 'true') { + runtimeOnly(files(rootProject.file("infinite_dimensions_jars/sodium-neoforge-0.8.13+mc1.21.1.jar"))) + } compileOnly(files(rootProject.file("infinite_dimensions_jars/antos-neoforge-1.21.1-${antos_version}.jar"))) runtimeOnly(files(rootProject.file("infinite_dimensions_jars/antos-neoforge-1.21.1-${antos_version}.jar"))) compileOnly("curse.maven:intapi-${intapi_curseforge_id}:${intapi_curseforge_file_id_neoforge}") diff --git a/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/AntarchyConfigModuleNeoforge.java b/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/AntarchyConfigModuleNeoforge.java index a9baa126..37e8c373 100644 --- a/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/AntarchyConfigModuleNeoforge.java +++ b/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/AntarchyConfigModuleNeoforge.java @@ -630,11 +630,6 @@ public final class AntarchyConfigModuleNeoforge { AntarchySettings.setCritterCageMaxCapturableWidth(AntarchyToolsConfig.critterCageMaxCapturableWidth()); AntarchySettings.setCritterCageMaxCapturableHeight(AntarchyToolsConfig.critterCageMaxCapturableHeight()); - AntarchySettings.setMinersDreamEnabled(AntarchyToolsConfig.minersDreamEnabled()); - AntarchySettings.setMinersDreamMinimumRange(AntarchyToolsConfig.minersDreamMinimumRange()); - AntarchySettings.setMinersDreamMaximumRange(AntarchyToolsConfig.minersDreamMaximumRange()); - AntarchySettings.setMinersDreamTorchSpacing(AntarchyToolsConfig.minersDreamTorchSpacing()); - AntarchySettings.setMinersDreamBlocksPerTick(AntarchyToolsConfig.minersDreamBlocksPerTick()); AntarchySettings.setDuctTapeRepairPercentPerUse(AntarchyToolsConfig.ductTapeRepairPercentPerUse()); AntarchySettings.setPotentNyxiteInvertedDurationSeconds(AntarchyToolsConfig.potentNyxiteInvertedDurationSeconds()); @@ -669,6 +664,15 @@ public final class AntarchyConfigModuleNeoforge { AntarchySettings.setElythiaPortalEnabled(AntarchyMiscConfig.elythiaPortalEnabled()); AntarchySettings.setThoraxisPortalEnabled(AntarchyMiscConfig.thoraxisPortalEnabled()); AntarchySettings.setCavarynPortalEnabled(AntarchyMiscConfig.cavarynPortalEnabled()); + AntarchySettings.setPortalGunCanFireThroughGlass(AntarchyMiscConfig.portalGunCanFireThroughGlass()); + AntarchySettings.setPortalGunCanFireThroughLiquid(AntarchyMiscConfig.portalGunCanFireThroughLiquid()); + AntarchySettings.setPortalGunCanPortalsResizeWhenCreated(AntarchyMiscConfig.portalGunCanPortalsResizeWhenCreated()); + AntarchySettings.setPortalGunCanPortalProjectilesChunkload(AntarchyMiscConfig.portalGunCanPortalProjectilesChunkload()); + AntarchySettings.setPortalGunSeeThroughPortals(AntarchyMiscConfig.portalGunSeeThroughPortals()); + AntarchySettings.setPortalGunFancyPortals(AntarchyMiscConfig.portalGunFancyPortals()); + AntarchySettings.setPortalGunMaxShootDistance(AntarchyMiscConfig.portalGunMaxShootDistance()); + AntarchySettings.setPortalGunEntityGrabWeightBase(AntarchyMiscConfig.portalGunEntityGrabWeightBase()); + AntarchySettings.setPortalGunIndicatorSize(AntarchyMiscConfig.portalGunIndicatorSize()); // Misc diff --git a/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/AntarchyMiscConfig.java b/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/AntarchyMiscConfig.java index ebbf6d9d..8e9b2e29 100644 --- a/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/AntarchyMiscConfig.java +++ b/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/AntarchyMiscConfig.java @@ -32,6 +32,15 @@ public final class AntarchyMiscConfig { private static final ModConfigSpec.BooleanValue ELYTHIA_PORTAL_ENABLED; private static final ModConfigSpec.BooleanValue THORAXIS_PORTAL_ENABLED; private static final ModConfigSpec.BooleanValue CAVARYN_PORTAL_ENABLED; + private static final ModConfigSpec.BooleanValue PORTAL_GUN_CAN_FIRE_THROUGH_GLASS; + private static final ModConfigSpec.BooleanValue PORTAL_GUN_CAN_FIRE_THROUGH_LIQUID; + private static final ModConfigSpec.BooleanValue PORTAL_GUN_CAN_PORTALS_RESIZE_WHEN_CREATED; + private static final ModConfigSpec.BooleanValue PORTAL_GUN_CAN_PORTAL_PROJECTILES_CHUNKLOAD; + private static final ModConfigSpec.BooleanValue PORTAL_GUN_SEE_THROUGH_PORTALS; + private static final ModConfigSpec.BooleanValue PORTAL_GUN_FANCY_PORTALS; + private static final ModConfigSpec.IntValue PORTAL_GUN_MAX_SHOOT_DISTANCE; + private static final ModConfigSpec.IntValue PORTAL_GUN_ENTITY_GRAB_WEIGHT_BASE; + private static final ModConfigSpec.IntValue PORTAL_GUN_INDICATOR_SIZE; // Diamond Minecart private static final ModConfigSpec.BooleanValue DIAMOND_MINECART_ENABLED; @@ -129,6 +138,18 @@ public final class AntarchyMiscConfig { CAVARYN_PORTAL_ENABLED = b.comment("Whether Myrmite portals to Cavaryn can activate and function.").define("cavarynPortalEnabled", true); b.pop(); + b.push("portalGun"); + PORTAL_GUN_CAN_FIRE_THROUGH_GLASS = b.define("canFireThroughGlass", false); + PORTAL_GUN_CAN_FIRE_THROUGH_LIQUID = b.define("canFireThroughLiquid", false); + PORTAL_GUN_CAN_PORTALS_RESIZE_WHEN_CREATED = b.define("canPortalsResizeWhenCreated", true); + PORTAL_GUN_CAN_PORTAL_PROJECTILES_CHUNKLOAD = b.define("canPortalProjectilesChunkload", true); + PORTAL_GUN_SEE_THROUGH_PORTALS = b.define("seeThroughPortals", true); + PORTAL_GUN_FANCY_PORTALS = b.define("fancyPortals", true); + PORTAL_GUN_MAX_SHOOT_DISTANCE = b.defineInRange("maxShootDistance", 10000, 1, Integer.MAX_VALUE); + PORTAL_GUN_ENTITY_GRAB_WEIGHT_BASE = b.defineInRange("entityGrabWeightBase", 100, 0, 1000); + PORTAL_GUN_INDICATOR_SIZE = b.defineInRange("indicatorSize", 30, 0, 100); + b.pop(); + // Diamond Minecart @@ -196,6 +217,15 @@ public final class AntarchyMiscConfig { public static boolean elythiaPortalEnabled() { return ELYTHIA_PORTAL_ENABLED.get(); } public static boolean thoraxisPortalEnabled() { return THORAXIS_PORTAL_ENABLED.get(); } public static boolean cavarynPortalEnabled() { return CAVARYN_PORTAL_ENABLED.get(); } + public static boolean portalGunCanFireThroughGlass() { return PORTAL_GUN_CAN_FIRE_THROUGH_GLASS.get(); } + public static boolean portalGunCanFireThroughLiquid() { return PORTAL_GUN_CAN_FIRE_THROUGH_LIQUID.get(); } + public static boolean portalGunCanPortalsResizeWhenCreated() { return PORTAL_GUN_CAN_PORTALS_RESIZE_WHEN_CREATED.get(); } + public static boolean portalGunCanPortalProjectilesChunkload() { return PORTAL_GUN_CAN_PORTAL_PROJECTILES_CHUNKLOAD.get(); } + public static boolean portalGunSeeThroughPortals() { return PORTAL_GUN_SEE_THROUGH_PORTALS.get(); } + public static boolean portalGunFancyPortals() { return PORTAL_GUN_FANCY_PORTALS.get(); } + public static int portalGunMaxShootDistance() { return PORTAL_GUN_MAX_SHOOT_DISTANCE.get(); } + public static int portalGunEntityGrabWeightBase() { return PORTAL_GUN_ENTITY_GRAB_WEIGHT_BASE.get(); } + public static int portalGunIndicatorSize() { return PORTAL_GUN_INDICATOR_SIZE.get(); } static boolean diamondMinecartEnabled() { return DIAMOND_MINECART_ENABLED.get(); } static boolean diamondMinecartPlacesRails() { return DIAMOND_MINECART_PLACES_RAILS.get(); } diff --git a/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/AntarchyNeoforgeEvents.java b/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/AntarchyNeoforgeEvents.java index 089dad02..3df0b55f 100644 --- a/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/AntarchyNeoforgeEvents.java +++ b/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/AntarchyNeoforgeEvents.java @@ -11,6 +11,7 @@ import com.craisinlord.antarchy.content.time.TimeDilationCommand; import net.neoforged.bus.api.SubscribeEvent; import net.neoforged.fml.common.EventBusSubscriber; import net.neoforged.neoforge.event.RegisterCommandsEvent; +import net.neoforged.neoforge.event.server.ServerStoppingEvent; import net.minecraft.commands.Commands; import net.minecraft.network.chat.Component; @@ -29,4 +30,9 @@ public final class AntarchyNeoforgeEvents { TimeDilationCommand.register(event.getDispatcher()); RoyalCommand.register(event.getDispatcher()); } + + @SubscribeEvent + public static void onServerStopping(ServerStoppingEvent event) { + com.craisinlord.antarchy.content.portalgun.PortalGunProjectileEntity.releaseAllProjectileTickets(); + } } diff --git a/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/AntarchyToolsConfig.java b/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/AntarchyToolsConfig.java index 419e1df7..eac2a651 100644 --- a/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/AntarchyToolsConfig.java +++ b/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/AntarchyToolsConfig.java @@ -258,11 +258,6 @@ public final class AntarchyToolsConfig { // Miner's Dream - private static final ModConfigSpec.BooleanValue MINERS_DREAM_ENABLED; - private static final ModConfigSpec.IntValue MINERS_DREAM_MINIMUM_RANGE; - private static final ModConfigSpec.IntValue MINERS_DREAM_MAXIMUM_RANGE; - private static final ModConfigSpec.IntValue MINERS_DREAM_TORCH_SPACING; - private static final ModConfigSpec.IntValue MINERS_DREAM_BLOCKS_PER_TICK; // Miscellaneous items / consumables @@ -635,16 +630,6 @@ public final class AntarchyToolsConfig { b.pop(); - // Miner's Dream - - b.push("minersDream"); - MINERS_DREAM_ENABLED = b.comment("Master toggle. If false, the item remains craftable but cannot be used.").define("enabled", true); - MINERS_DREAM_MINIMUM_RANGE = b.comment("Minimum cave length in blocks. Clamped to [8, 256].") .defineInRange("minimumRange", 48, 8, 256); - MINERS_DREAM_MAXIMUM_RANGE = b.comment("Maximum cave length in blocks. Clamped to [8, 256].") .defineInRange("maximumRange", 96, 8, 256); - MINERS_DREAM_TORCH_SPACING = b.comment("Approximate blocks between torch placements along the cave.") .defineInRange("torchSpacing", 10, 4, 64); - MINERS_DREAM_BLOCKS_PER_TICK = b.comment("Maximum blocks removed per server tick while excavating.") .defineInRange("blocksPerTick", 150, 1, 2000); - b.pop(); - // Miscellaneous items / consumables @@ -833,11 +818,6 @@ public final class AntarchyToolsConfig { static double critterCageMaxCapturableWidth() { return CRITTER_CAGE_MAX_CAPTURABLE_WIDTH.get(); } static double critterCageMaxCapturableHeight() { return CRITTER_CAGE_MAX_CAPTURABLE_HEIGHT.get(); } - static boolean minersDreamEnabled() { return MINERS_DREAM_ENABLED.get(); } - static int minersDreamMinimumRange() { return MINERS_DREAM_MINIMUM_RANGE.get(); } - static int minersDreamMaximumRange() { return MINERS_DREAM_MAXIMUM_RANGE.get(); } - static int minersDreamTorchSpacing() { return MINERS_DREAM_TORCH_SPACING.get(); } - static int minersDreamBlocksPerTick() { return MINERS_DREAM_BLOCKS_PER_TICK.get(); } static double ductTapeRepairPercentPerUse() { return DUCT_TAPE_REPAIR_PERCENT_PER_USE.get(); } static double potentNyxiteInvertedDurationSeconds() { return POTENT_NYXITE_INVERTED_DURATION_SECONDS.get(); } diff --git a/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/client/AntarchyKeyBindings.java b/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/client/AntarchyKeyBindings.java index 2a2757bb..86a23f83 100644 --- a/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/client/AntarchyKeyBindings.java +++ b/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/client/AntarchyKeyBindings.java @@ -58,6 +58,27 @@ public final class AntarchyKeyBindings { InputConstants.KEY_B, CATEGORY ); + public static final KeyMapping PORTAL_GUN_ZOOM = new KeyMapping( + "key.antarchy.portal_gun_zoom", + KeyConflictContext.IN_GAME, + InputConstants.Type.MOUSE, + InputConstants.MOUSE_BUTTON_MIDDLE, + CATEGORY + ); + public static final KeyMapping PORTAL_GUN_GRAB = new KeyMapping( + "key.antarchy.portal_gun_grab", + KeyConflictContext.IN_GAME, + InputConstants.Type.KEYSYM, + InputConstants.KEY_G, + CATEGORY + ); + public static final KeyMapping PORTAL_GUN_RESET = new KeyMapping( + "key.antarchy.portal_gun_reset", + KeyConflictContext.IN_GAME, + InputConstants.Type.KEYSYM, + InputConstants.KEY_R, + CATEGORY + ); private AntarchyKeyBindings() {} @@ -69,6 +90,9 @@ public final class AntarchyKeyBindings { event.register(TIGERS_EYE_CAMOUFLAGE); event.register(ROYAL_INVERSION_TOGGLE); event.register(ROYAL_BOUNDARY); + event.register(PORTAL_GUN_ZOOM); + event.register(PORTAL_GUN_GRAB); + event.register(PORTAL_GUN_RESET); } public static boolean isMountFlightTogglePressed() { @@ -90,4 +114,20 @@ public final class AntarchyKeyBindings { public static boolean consumeRoyalBoundaryPressed() { return ROYAL_BOUNDARY.consumeClick(); } + + public static boolean consumePortalGunZoomPressed() { + return PORTAL_GUN_ZOOM.consumeClick(); + } + + public static boolean consumePortalGunGrabPressed() { + return PORTAL_GUN_GRAB.consumeClick(); + } + + public static boolean consumePortalGunResetPressed() { + return PORTAL_GUN_RESET.consumeClick(); + } + + public static boolean isPortalGunResetDown() { + return PORTAL_GUN_RESET.isDown() || PORTAL_GUN_RESET.consumeClick(); + } } diff --git a/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/client/AntarchyNeoForgeClient.java b/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/client/AntarchyNeoForgeClient.java index 6bc5d737..23d39194 100644 --- a/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/client/AntarchyNeoForgeClient.java +++ b/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/client/AntarchyNeoForgeClient.java @@ -2,6 +2,7 @@ package com.craisinlord.antarchy.neoforge.client; import com.craisinlord.antarchy.Antarchy; import com.craisinlord.antarchy.content.client.GoopedHudRenderer; +import com.craisinlord.antarchy.content.client.PortalGunCrosshairRenderer; import com.craisinlord.antarchy.content.client.AntarchyArchivePreviews; import com.craisinlord.antarchy.content.client.game.AntarchyComputerGames; import com.craisinlord.antarchy.content.client.HordeHudRenderer; @@ -16,6 +17,7 @@ import com.craisinlord.antarchy.neoforge.registry.AntarchyNeoforgeItems; import com.craisinlord.antarchy.neoforge.registry.AntarchyNeoforgeMisc; import com.mojang.blaze3d.shaders.FogShape; import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.DefaultVertexFormat; import com.mojang.blaze3d.vertex.VertexConsumer; import net.minecraft.client.model.BoatModel; import net.minecraft.client.model.ArmorStandModel; @@ -38,6 +40,7 @@ import net.minecraft.client.renderer.BiomeColors; import net.minecraft.client.renderer.BlockEntityWithoutLevelRenderer; import net.minecraft.client.renderer.ItemBlockRenderTypes; import net.minecraft.client.renderer.RenderType; +import net.minecraft.client.renderer.ShaderInstance; import net.minecraft.client.renderer.blockentity.CampfireRenderer; import net.minecraft.client.renderer.entity.LivingEntityRenderer; import net.minecraft.client.renderer.item.ItemProperties; @@ -56,6 +59,7 @@ 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.RegisterParticleProvidersEvent; +import net.neoforged.neoforge.client.event.RegisterShadersEvent; import net.neoforged.neoforge.client.event.RegisterColorHandlersEvent; import net.neoforged.neoforge.client.event.RegisterMenuScreensEvent; import net.neoforged.neoforge.client.event.EntityRenderersEvent; @@ -98,6 +102,19 @@ public final class AntarchyNeoForgeClient { private AntarchyNeoForgeClient() { } + @SubscribeEvent + public static void registerPortalViewShader(RegisterShadersEvent event) { + PortalGunPortalViewRenderer.clearPortalViewShader(); + try { + event.registerShader( + new ShaderInstance(event.getResourceProvider(), ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "portal_view"), DefaultVertexFormat.POSITION_TEX), + PortalGunPortalViewRenderer::setPortalViewShader + ); + } catch (java.io.IOException | RuntimeException exception) { + Antarchy.LOGGER.error("Failed to register portal view shader", exception); + } + } + @SubscribeEvent public static void clientSetup(FMLClientSetupEvent event) { Antarchy.physicalClient = true; @@ -368,6 +385,11 @@ public final class AntarchyNeoForgeClient { @SubscribeEvent public static void registerGuiLayers(RegisterGuiLayersEvent event) { + event.registerAbove( + VanillaGuiLayers.CROSSHAIR, + ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "portal_gun_crosshair"), + (guiGraphics, partialTick) -> PortalGunCrosshairRenderer.render(guiGraphics) + ); event.registerAboveAll( ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "dread_darkness"), (guiGraphics, partialTick) -> DreadHudRenderer.render(guiGraphics) diff --git a/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/client/PortalGunClientHandler.java b/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/client/PortalGunClientHandler.java index d43e82f1..3bc0efee 100644 --- a/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/client/PortalGunClientHandler.java +++ b/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/client/PortalGunClientHandler.java @@ -2,7 +2,9 @@ package com.craisinlord.antarchy.neoforge.client; import com.craisinlord.antarchy.Antarchy; import com.craisinlord.antarchy.content.item.PortalGunItem; +import com.craisinlord.antarchy.content.client.PortalGunZoomClientState; import com.craisinlord.antarchy.content.network.PortalGunPrimaryPayload; +import com.craisinlord.antarchy.content.network.PortalGunGrabPayload; import net.minecraft.client.Minecraft; import net.neoforged.api.distmarker.Dist; import net.neoforged.bus.api.SubscribeEvent; @@ -20,19 +22,40 @@ public final class PortalGunClientHandler { @SubscribeEvent public static void onClientTick(ClientTickEvent.Post event) { Minecraft mc = Minecraft.getInstance(); + if (mc.player == null) { + com.craisinlord.antarchy.content.client.PortalGunGrabClientState.clear(); + com.craisinlord.antarchy.content.client.PortalGunResetClientState.clear(); + } + com.craisinlord.antarchy.content.client.PortalGunIndicatorClientState.setRequestSender(net.neoforged.neoforge.network.PacketDistributor::sendToServer); + boolean hasPortalGun = mc.player != null && (mc.player.getMainHandItem().getItem() instanceof PortalGunItem || mc.player.getOffhandItem().getItem() instanceof PortalGunItem); + boolean gameplayInput = mc.screen == null; + boolean held = hasPortalGun && gameplayInput; + boolean zoomPressed = AntarchyKeyBindings.consumePortalGunZoomPressed(); + PortalGunZoomClientState.tick(mc, hasPortalGun, zoomPressed && gameplayInput); + boolean resetActive = com.craisinlord.antarchy.content.client.PortalGunResetClientState.tick( + mc, + held, + AntarchyKeyBindings.isPortalGunResetDown(), + net.neoforged.neoforge.network.PacketDistributor::sendToServer + ); + if (held && AntarchyKeyBindings.consumePortalGunGrabPressed()) { + PacketDistributor.sendToServer(new PortalGunGrabPayload()); + } if (mc.player == null || mc.level == null || mc.screen != null) { lastAttackDown = false; return; } - if (!(mc.player.getMainHandItem().getItem() instanceof PortalGunItem)) { + boolean offhand = !(mc.player.getMainHandItem().getItem() instanceof PortalGunItem) + && mc.player.getOffhandItem().getItem() instanceof PortalGunItem; + if (!offhand && !(mc.player.getMainHandItem().getItem() instanceof PortalGunItem)) { lastAttackDown = false; return; } boolean attackDown = mc.options.keyAttack.isDown(); - if (attackDown && !lastAttackDown) { - PacketDistributor.sendToServer(new PortalGunPrimaryPayload()); + if (attackDown && !lastAttackDown && !resetActive) { + PacketDistributor.sendToServer(new PortalGunPrimaryPayload(offhand)); } lastAttackDown = attackDown; } diff --git a/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/client/PortalGunPortalViewRenderHandler.java b/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/client/PortalGunPortalViewRenderHandler.java index ef70aea2..cc38226c 100644 --- a/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/client/PortalGunPortalViewRenderHandler.java +++ b/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/client/PortalGunPortalViewRenderHandler.java @@ -2,26 +2,24 @@ package com.craisinlord.antarchy.neoforge.client; import com.craisinlord.antarchy.Antarchy; import com.craisinlord.antarchy.content.client.renderer.PortalGunPortalViewRenderer; +import net.neoforged.api.distmarker.Dist; +import net.neoforged.bus.api.SubscribeEvent; +import net.neoforged.fml.common.EventBusSubscriber; import net.neoforged.neoforge.client.event.RenderLevelStageEvent; +@EventBusSubscriber(modid = Antarchy.MODID, value = Dist.CLIENT) public final class PortalGunPortalViewRenderHandler { - private static long lastStageLogNanos; - private PortalGunPortalViewRenderHandler() { } + @SubscribeEvent public static void onRenderLevelStage(RenderLevelStageEvent event) { if (!PortalGunPortalViewRenderer.isEnabled()) { return; } - if (event.getStage() != RenderLevelStageEvent.Stage.AFTER_PARTICLES) { + if (event.getStage() != RenderLevelStageEvent.Stage.AFTER_BLOCK_ENTITIES) { return; } - long now = System.nanoTime(); - if (now - lastStageLogNanos > 5_000_000_000L) { - lastStageLogNanos = now; - Antarchy.LOGGER.info("Portal gun NeoForge render stage fired stage={} partial={}", event.getStage(), event.getPartialTick().getGameTimeDeltaPartialTick(false)); - } - PortalGunPortalViewRenderer.render(event.getCamera(), event.getPoseStack().last().pose(), event.getPartialTick()); + PortalGunPortalViewRenderer.render(event.getCamera(), event.getModelViewMatrix(), event.getProjectionMatrix(), event.getPartialTick()); } } diff --git a/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/network/AntarchyGravityNetworking.java b/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/network/AntarchyGravityNetworking.java index 4332c90a..1b165d81 100644 --- a/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/network/AntarchyGravityNetworking.java +++ b/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/network/AntarchyGravityNetworking.java @@ -19,6 +19,7 @@ import com.craisinlord.antarchy.content.network.PortalGunPrimaryPayload; import com.craisinlord.antarchy.content.client.CameraShakeClientState; import net.minecraft.world.entity.Entity; import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.item.ItemStack; import net.minecraft.world.phys.Vec3; import net.neoforged.neoforge.network.PacketDistributor; import net.neoforged.neoforge.network.handling.IPayloadContext; @@ -29,7 +30,21 @@ public final class AntarchyGravityNetworking { } public static void register(PayloadRegistrar registrar) { + com.craisinlord.antarchy.content.portalgun.PortalGunIndicatorSync.setSender(PacketDistributor::sendToPlayer); + com.craisinlord.antarchy.content.portalgun.PortalGunGrabManager.setStateSender(PacketDistributor::sendToPlayer); registrar.playToClient( + com.craisinlord.antarchy.content.network.PortalGunIndicatorPayload.TYPE, + com.craisinlord.antarchy.content.network.PortalGunIndicatorPayload.STREAM_CODEC, + (payload, context) -> context.enqueueWork(() -> com.craisinlord.antarchy.content.client.PortalGunIndicatorClientState.update(payload)) + ).playToServer( + com.craisinlord.antarchy.content.network.PortalGunIndicatorRequestPayload.TYPE, + com.craisinlord.antarchy.content.network.PortalGunIndicatorRequestPayload.STREAM_CODEC, + AntarchyGravityNetworking::handlePortalGunIndicatorRequest + ).playToClient( + com.craisinlord.antarchy.content.network.PortalGunGrabStatePayload.TYPE, + com.craisinlord.antarchy.content.network.PortalGunGrabStatePayload.STREAM_CODEC, + (payload, context) -> context.enqueueWork(() -> com.craisinlord.antarchy.content.client.PortalGunGrabClientState.update(payload.active())) + ).playToClient( GravityStatePayload.TYPE, GravityStatePayload.STREAM_CODEC, AntarchyGravityNetworking::handleGravityState @@ -45,6 +60,18 @@ public final class AntarchyGravityNetworking { PortalGunPrimaryPayload.TYPE, PortalGunPrimaryPayload.STREAM_CODEC, AntarchyGravityNetworking::handlePortalGunPrimary + ).playToServer( + com.craisinlord.antarchy.content.network.PortalGunGrabPayload.TYPE, + com.craisinlord.antarchy.content.network.PortalGunGrabPayload.STREAM_CODEC, + AntarchyGravityNetworking::handlePortalGunGrab + ).playToServer( + com.craisinlord.antarchy.content.network.PortalGunResetPayload.TYPE, + com.craisinlord.antarchy.content.network.PortalGunResetPayload.STREAM_CODEC, + (payload, context) -> context.enqueueWork(() -> { + if (context.player() instanceof ServerPlayer serverPlayer) { + com.craisinlord.antarchy.content.portalgun.PortalGunResetManager.handleInput(serverPlayer, payload.action()); + } + }) ).playToServer( EyeOfStormPrimaryPayload.TYPE, EyeOfStormPrimaryPayload.STREAM_CODEC, @@ -203,11 +230,28 @@ public final class AntarchyGravityNetworking { return; } - if (!(serverPlayer.getMainHandItem().getItem() instanceof PortalGunItem portalGunItem)) { + ItemStack stack = payload.offhand() ? serverPlayer.getOffhandItem() : serverPlayer.getMainHandItem(); + if (!(stack.getItem() instanceof PortalGunItem portalGunItem)) { return; } - portalGunItem.firePrimary(serverPlayer.serverLevel(), serverPlayer, serverPlayer.getMainHandItem()); + portalGunItem.firePrimary(serverPlayer.serverLevel(), serverPlayer, stack, payload.offhand()); + }); + } + + private static void handlePortalGunGrab(com.craisinlord.antarchy.content.network.PortalGunGrabPayload payload, IPayloadContext context) { + context.enqueueWork(() -> { + if (context.player() instanceof ServerPlayer serverPlayer) { + com.craisinlord.antarchy.content.portalgun.PortalGunGrabManager.toggle(serverPlayer); + } + }); + } + + private static void handlePortalGunIndicatorRequest(com.craisinlord.antarchy.content.network.PortalGunIndicatorRequestPayload payload, IPayloadContext context) { + context.enqueueWork(() -> { + if (context.player() instanceof ServerPlayer serverPlayer) { + com.craisinlord.antarchy.content.portalgun.PortalGunIndicatorSync.handleRequest(serverPlayer, payload); + } }); } diff --git a/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/registry/AntarchyNeoForgeEvents.java b/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/registry/AntarchyNeoForgeEvents.java index 978cc30d..2fdcba3f 100644 --- a/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/registry/AntarchyNeoForgeEvents.java +++ b/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/registry/AntarchyNeoForgeEvents.java @@ -88,29 +88,18 @@ import net.neoforged.neoforge.event.village.VillagerTradesEvent; import net.neoforged.neoforge.event.village.WandererTradesEvent; import net.neoforged.neoforge.event.tick.PlayerTickEvent; import net.neoforged.neoforge.event.tick.ServerTickEvent; -import com.craisinlord.antarchy.content.item.MinersDreamExcavationManager; import net.neoforged.neoforge.fluids.FluidInteractionRegistry; import net.neoforged.neoforge.network.PacketDistributor; public final class AntarchyNeoForgeEvents { - private static final long SERVER_HANG_WARN_NANOS = 5_000_000_000L; - private static final long SERVER_IDLE_BUSY_WARN_NANOS = 30_000_000_000L; - private static final long SERVER_HANG_REPEAT_NANOS = 30_000_000_000L; private static final double OVERHEAD_INVERSION_WARN_MS = 10.0D; private static final long OVERHEAD_INVERSION_WARN_INTERVAL_TICKS = 100L; - private static volatile boolean serverHangWatchdogStarted; - private static volatile long serverTickStartNanos; - private static volatile long serverTickEndNanos; - private static volatile long serverHangLastDumpNanos; - private static volatile Thread serverTickThread; private static long lastOverheadInversionWarnTick = Long.MIN_VALUE / 2L; private AntarchyNeoForgeEvents() {} public static void register(IEventBus modEventBus) { modEventBus.addListener(AntarchyNeoForgeEvents::onCommonSetup); - NeoForge.EVENT_BUS.addListener(AntarchyNeoForgeEvents::beginServerTickWatchdog); - NeoForge.EVENT_BUS.addListener(AntarchyNeoForgeEvents::endServerTickWatchdog); NeoForge.EVENT_BUS.addListener(AntarchyNeoForgeEvents::onMissileSquidDeath); NeoForge.EVENT_BUS.addListener(AntarchyNeoForgeEvents::onPermanentPortalSacrifice); NeoForge.EVENT_BUS.addListener(AntarchyNeoForgeEvents::onLivingBreathe); @@ -126,6 +115,7 @@ public final class AntarchyNeoForgeEvents { NeoForge.EVENT_BUS.addListener(AntarchyNeoForgeEvents::handleBossMagicBurstMitigation); NeoForge.EVENT_BUS.addListener(AntarchyNeoForgeEvents::handleScorpionWhipAttackEntity); NeoForge.EVENT_BUS.addListener(AntarchyNeoForgeEvents::handleAttitudeAdjusterAttackEntity); + NeoForge.EVENT_BUS.addListener(AntarchyNeoForgeEvents::handlePortalGunAttackEntity); NeoForge.EVENT_BUS.addListener(AntarchyNeoForgeEvents::handleScorpionWhipLeftClickBlock); NeoForge.EVENT_BUS.addListener(AntarchyNeoForgeEvents::handlePortalGunLeftClickBlock); NeoForge.EVENT_BUS.addListener(AntarchyNeoForgeEvents::handleScorpionWhipRightClickBlock); @@ -140,7 +130,7 @@ public final class AntarchyNeoForgeEvents { NeoForge.EVENT_BUS.addListener(AntarchyNeoForgeEvents::tickDreadAndIchor); NeoForge.EVENT_BUS.addListener(AntarchyNeoForgeEvents::tickScorpionWhips); NeoForge.EVENT_BUS.addListener(AntarchyNeoForgeEvents::tickWormHooks); - NeoForge.EVENT_BUS.addListener(AntarchyNeoForgeEvents::tickMinersDreamExcavations); + NeoForge.EVENT_BUS.addListener(AntarchyNeoForgeEvents::tickAttitudeAdjusterSlams); NeoForge.EVENT_BUS.addListener(AntarchyNeoForgeEvents::handleAntiwaterDamage); NeoForge.EVENT_BUS.addListener(AntarchyNeoForgeEvents::handleAntiwaterFall); NeoForge.EVENT_BUS.addListener(AntarchyNeoForgeEvents::handleBloodCrystalBootsFall); @@ -178,92 +168,6 @@ public final class AntarchyNeoForgeEvents { NeoForge.EVENT_BUS.addListener(AntarchyNeoForgeEvents::onWandererTrades); } - static void beginServerTickWatchdog(ServerTickEvent.Pre event) { - ensureServerHangWatchdogStarted(); - serverTickThread = Thread.currentThread(); - serverTickStartNanos = System.nanoTime(); - } - - static void endServerTickWatchdog(ServerTickEvent.Post event) { - serverTickEndNanos = System.nanoTime(); - serverTickStartNanos = 0L; - } - - private static synchronized void ensureServerHangWatchdogStarted() { - if (serverHangWatchdogStarted) { - return; - } - serverHangWatchdogStarted = true; - Thread watchdog = new Thread(() -> { - while (true) { - try { - Thread.sleep(2_000L); - } catch (InterruptedException ignored) { - Thread.currentThread().interrupt(); - return; - } - - long tickStart = serverTickStartNanos; - long tickEnd = serverTickEndNanos; - Thread tickThread = serverTickThread; - long now = System.nanoTime(); - if (tickThread == null || now - serverHangLastDumpNanos < SERVER_HANG_REPEAT_NANOS) { - continue; - } - - if (tickStart > 0L && now - tickStart >= SERVER_HANG_WARN_NANOS) { - antarchy$dumpServerThreadWatchdog( - "server thread appears hung", - (now - tickStart) / 1_000_000.0D, - tickThread - ); - serverHangLastDumpNanos = now; - continue; - } - - Thread.State state = tickThread.getState(); - boolean idleBusy = tickStart <= 0L - && tickEnd > 0L - && now - tickEnd >= SERVER_IDLE_BUSY_WARN_NANOS - && state != Thread.State.WAITING - && state != Thread.State.TIMED_WAITING - && !antarchy$isServerThreadIdleWait(tickThread); - if (idleBusy) { - antarchy$dumpServerThreadWatchdog( - "server thread busy outside active tick", - (now - tickEnd) / 1_000_000.0D, - tickThread - ); - serverHangLastDumpNanos = now; - } - } - }, "Antarchy Server Hang Watchdog"); - watchdog.setDaemon(true); - watchdog.start(); - } - - private static void antarchy$dumpServerThreadWatchdog(String message, double elapsedMs, Thread tickThread) { - Antarchy.LOGGER.error( - "[antarchy-watchdog] {} elapsedMs={} thread={} state={}", - message, elapsedMs, tickThread.getName(), tickThread.getState() - ); - for (StackTraceElement element : tickThread.getStackTrace()) { - Antarchy.LOGGER.error("[antarchy-watchdog] at {}", element); - } - } - - private static boolean antarchy$isServerThreadIdleWait(Thread tickThread) { - for (StackTraceElement element : tickThread.getStackTrace()) { - String method = element.getMethodName(); - if (method.equals("waitUntilNextTick") - || method.equals("waitForTasks") - || method.equals("managedBlock")) { - return true; - } - } - return false; - } - static void onVillagerTrades(VillagerTradesEvent event) { if (event.getType() == AntarchyNeoforgeMisc.COMPUTER_SCIENTIST.get()) { int[] slots = ComputerScientistTradeManager.slotsPerLevel(); @@ -1093,6 +997,13 @@ public final class AntarchyNeoForgeEvents { AttitudeAdjusterSlamManager.markSpecialHit(player); } + static void handlePortalGunAttackEntity(AttackEntityEvent event) { + if (event.getEntity().getMainHandItem().getItem() instanceof com.craisinlord.antarchy.content.item.PortalGunItem + || event.getEntity().getOffhandItem().getItem() instanceof com.craisinlord.antarchy.content.item.PortalGunItem) { + event.setCanceled(true); + } + } + static void handleScorpionWhipLeftClickBlock(PlayerInteractEvent.LeftClickBlock event) { if (!(event.getEntity() instanceof ServerPlayer player)) { return; @@ -1216,10 +1127,9 @@ public final class AntarchyNeoForgeEvents { } } - static void tickMinersDreamExcavations(ServerTickEvent.Post event) { + static void tickAttitudeAdjusterSlams(ServerTickEvent.Post event) { for (ServerLevel level : event.getServer().getAllLevels()) { AttitudeAdjusterSlamManager.tick(level); - MinersDreamExcavationManager.tick(level); } } diff --git a/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/registry/AntarchyNeoforgeSounds.java b/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/registry/AntarchyNeoforgeSounds.java index a127f2b0..3b5bb707 100644 --- a/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/registry/AntarchyNeoforgeSounds.java +++ b/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/registry/AntarchyNeoforgeSounds.java @@ -127,6 +127,9 @@ public final class AntarchyNeoforgeSounds { public static final DeferredHolder PORTAL_OPEN_BLUE = registerSoundEvent("portal_open_blue"); public static final DeferredHolder PORTAL_OPEN_ORANGE = registerSoundEvent("portal_open_orange"); public static final DeferredHolder PORTAL_FIZZLE = registerSoundEvent("portal_fizzle"); + public static final DeferredHolder PORTAL_ENTER = registerSoundEvent("portal_enter"); + public static final DeferredHolder PORTAL_EXIT = registerSoundEvent("portal_exit"); + public static final DeferredHolder PORTAL_AMBIENT = registerSoundEvent("portal_ambient"); public static final DeferredHolder BED_BUG_AMBIENT = registerSoundEvent("bed_bug_ambient"); public static final DeferredHolder BED_BUG_HURT = registerSoundEvent("bed_bug_hurt"); public static final DeferredHolder BED_BUG_ATTACK = registerSoundEvent("bed_bug_attack"); diff --git a/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/registry/AntarchyNeoforgeSpawnPlacements.java b/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/registry/AntarchyNeoforgeSpawnPlacements.java index 8fc65ac0..da0dbd2a 100644 --- a/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/registry/AntarchyNeoforgeSpawnPlacements.java +++ b/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/registry/AntarchyNeoforgeSpawnPlacements.java @@ -51,7 +51,7 @@ public class AntarchyNeoforgeSpawnPlacements { event.register(AntarchyNeoforgeEntites.VORTEX.get(), SpawnPlacementTypes.NO_RESTRICTIONS, Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, com.craisinlord.antarchy.content.entity.vortex.VortexEntity::canSpawn, RegisterSpawnPlacementsEvent.Operation.OR); event.register(AntarchyNeoforgeEntites.SCORPION.get(), SpawnPlacementTypes.ON_GROUND, Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, ScorpionEntity::canSpawn, RegisterSpawnPlacementsEvent.Operation.OR); event.register(AntarchyNeoforgeEntites.BASILISK.get(), SpawnPlacementTypes.ON_GROUND, Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, BasiliskEntity::canSpawn, RegisterSpawnPlacementsEvent.Operation.OR); - event.register(AntarchyNeoforgeEntites.EMPEROR_SCORPION.get(), SpawnPlacementTypes.ON_GROUND, Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, EmperorScorpionEntity::canSpawn, RegisterSpawnPlacementsEvent.Operation.OR); + event.register(AntarchyNeoforgeEntites.EMPEROR_SCORPION.get(), SpawnPlacementTypes.NO_RESTRICTIONS, Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, EmperorScorpionEntity::canSpawn, RegisterSpawnPlacementsEvent.Operation.OR); event.register(AntarchyNeoforgeEntites.CRAWLING_BLIGHT.get(), SpawnPlacementTypes.ON_GROUND, Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, CrawlingBlightEntity::canSpawn, RegisterSpawnPlacementsEvent.Operation.OR); event.register(AntarchyNeoforgeEntites.SKULKING_FRIGHT.get(), SpawnPlacementTypes.ON_GROUND, Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, SkulkingFrightEntity::canSpawn, RegisterSpawnPlacementsEvent.Operation.OR); event.register(AntarchyNeoforgeEntites.MANTICORE.get(), SpawnPlacementTypes.NO_RESTRICTIONS, Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, com.craisinlord.antarchy.content.entity.ManticoreEntity::canSpawn, RegisterSpawnPlacementsEvent.Operation.OR); diff --git a/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/registry/PortalGunGrabEvents.java b/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/registry/PortalGunGrabEvents.java new file mode 100644 index 00000000..c9cd49f4 --- /dev/null +++ b/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/registry/PortalGunGrabEvents.java @@ -0,0 +1,25 @@ +package com.craisinlord.antarchy.neoforge.registry; + +import com.craisinlord.antarchy.Antarchy; +import com.craisinlord.antarchy.content.portalgun.PortalGunGrabManager; +import net.neoforged.bus.api.SubscribeEvent; +import net.neoforged.fml.common.EventBusSubscriber; +import net.neoforged.neoforge.event.server.ServerStoppingEvent; +import net.neoforged.neoforge.event.tick.ServerTickEvent; + +@EventBusSubscriber(modid = Antarchy.MODID) +public final class PortalGunGrabEvents { + private PortalGunGrabEvents() { + } + + @SubscribeEvent + public static void onServerTick(ServerTickEvent.Post event) { + PortalGunGrabManager.tick(event.getServer()); + } + + @SubscribeEvent + public static void onServerStopping(ServerStoppingEvent event) { + PortalGunGrabManager.clear(event.getServer()); + com.craisinlord.antarchy.content.portalgun.PortalGunResetManager.clear(event.getServer()); + } +} diff --git a/neoforge-1.21.1/src/main/resources/META-INF/neoforge.mods.toml b/neoforge-1.21.1/src/main/resources/META-INF/neoforge.mods.toml index c05d200d..54dcfd04 100644 --- a/neoforge-1.21.1/src/main/resources/META-INF/neoforge.mods.toml +++ b/neoforge-1.21.1/src/main/resources/META-INF/neoforge.mods.toml @@ -17,6 +17,9 @@ displayURL="https://www.curseforge.com/minecraft/mc-mods/antarchy" [[mixins]] config = "antarchy-common.mixins.json" +[[mixins]] + config = "antarchy-sodium.mixins.json" + [[mixins]] config = "antarchy-neoforge.mixins.json" @@ -68,6 +71,22 @@ modId="antos" side="BOTH" reason="Antarchy requires Infinite Dimensions 2.7.3 or newer" +[[dependencies.antarchy]] + modId="apple_cows" + type="incompatible" + versionRange="*" + ordering="NONE" + side="BOTH" + reason="Antarchy already includes Apple Cows. Remove the standalone Apple Cows mod." + +[[dependencies.antarchy]] + modId="hoverboards" + type="incompatible" + versionRange="*" + ordering="NONE" + side="BOTH" + reason="Antarchy already includes Hoverboards. Remove the standalone Hoverboards mod." + [[dependencies.antarchy]] modId="configured" type="optional" diff --git a/settings.gradle b/settings.gradle index 4e2803e8..e5ba5dc9 100644 --- a/settings.gradle +++ b/settings.gradle @@ -8,7 +8,7 @@ pluginManagement { gradlePluginPortal() } plugins { - id 'net.fabricmc.fabric-loom-remap' version '1.15.5' + id 'net.fabricmc.fabric-loom-remap' version '1.16.3' } }