portal gun fixes and variants

This commit is contained in:
craisinlord committed 2026-10-08 13:54:21 -05:00
1 parent 2d90cb8aaf
commit 216efeb39b
111 files changed
+1937 -2136

No files matched your search

@@ -143,8 +143,9 @@ public final class AntarchyConfigCatalog {
|| name.equals("portalGunCanPortalsResizeWhenCreated")
|| name.equals("portalGunCanPortalProjectilesChunkload")
|| name.equals("portalGunMaxShootDistance")
|| name.equals("portalGunEntityGrabWeightBase")
|| name.equals("portalGunIndicatorSize")
|| name.equals("portalGunMinFloorExitSpeed")
|| name.equals("portalGunMaxLiveViews")
|| name.equals("portalGunMaxExitSpeed")
|| name.equals("hushweedSporeLifetimeSeconds")
|| name.equals("elythiaFireflyParticlesEnabled")
|| name.equals("duplicatorTreeEnabled")
@@ -555,9 +555,10 @@ public final class AntarchySettings {
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 double portalGunMinFloorExitSpeed = 0.6D;
private static int portalGunMaxLiveViews = 8;
private static double portalGunMaxExitSpeed = 0.0D;
private static boolean elythiaFireflyParticlesEnabled = true;
private static int bloodCrystalArmorShieldRechargeTicks = 600;
private static int bloodCrystalAppleShieldCount = 2;
@@ -1335,16 +1336,20 @@ public final class AntarchySettings {
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 void setPortalGunMaxLiveViews(int value) {
portalGunMaxLiveViews = Math.max(0, Math.min(32, value));
}
public static void setPortalGunMinFloorExitSpeed(double value) {
portalGunMinFloorExitSpeed = Math.max(0.0D, Math.min(4.0D, value));
}
public static void setPortalGunMaxExitSpeed(double value) {
portalGunMaxExitSpeed = Math.max(0.0D, Math.min(100.0D, value));
}
public static boolean permanentPortalsEnabled() { return permanentPortalsEnabled; }
@@ -1358,9 +1363,10 @@ public final class AntarchySettings {
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 double portalGunMinFloorExitSpeed() { return portalGunMinFloorExitSpeed; }
public static int portalGunMaxLiveViews() { return portalGunMaxLiveViews; }
public static double portalGunMaxExitSpeed() { return portalGunMaxExitSpeed; }
public static void setBrownAntRequiresReagent(boolean value) {
brownAntRequiresReagent = value;
@@ -6,23 +6,14 @@ import com.craisinlord.antarchy.content.AntarchyObjects;
import com.craisinlord.antarchy.content.AntarchySoundEvents;
import com.craisinlord.antarchy.content.AntarchyTags;
import com.mojang.serialization.MapCodec;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.sounds.SoundSource;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.ItemInteractionResult;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.item.ItemEntity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.DiggerItem;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.ShearsItem;
import net.minecraft.world.item.SwordItem;
import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.Level;
@@ -38,9 +29,7 @@ import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraft.world.level.block.state.properties.DirectionProperty;
import net.minecraft.world.level.block.state.properties.EnumProperty;
import net.minecraft.world.level.block.state.properties.IntegerProperty;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.Vec3;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.Shapes;
import net.minecraft.world.phys.shapes.VoxelShape;
@@ -54,24 +43,6 @@ public class DuctTapeBlock extends Block {
public static final DirectionProperty FACING = BlockStateProperties.HORIZONTAL_FACING;
private static final int TAPE_HEIGHT = 2;
private static final long STICK_SOUND_COOLDOWN_TICKS = 10L;
private static final long TAPE_DECAY_INTERVAL_TICKS = 30L;
private static final double FLOOR_TRIGGER_HEIGHT = 0.30D;
private static final double WALL_TRIGGER_DEPTH = 0.25D;
private static final double TRIGGER_PAD = 0.12D;
private static final double WALL_Y_PAD = 0.12D;
private static final double WALL_SIDE_PAD = 0.08D;
private static final double WALL_INNER_INSET = 0.03D;
private static final double TRIGGER_INFLATE_DEFAULT = 0.15D;
private static final int SEARCH_INTERVAL_TICKS = 4;
private static final double SEARCH_MOTION_THRESHOLD_SQR = 1.0E-8D;
private static final double SEARCH_PAD_XZ = Math.max(WALL_TRIGGER_DEPTH, TRIGGER_PAD);
private static final double SEARCH_PAD_Y = FLOOR_TRIGGER_HEIGHT;
private static final Map<UUID, Long> STICK_SOUND_TIMES = new HashMap<>();
private static final Map<UUID, String> STICK_CONTACT_KEYS = new HashMap<>();
private static final Map<String, Long> TAPE_DECAY_TIMES = new HashMap<>();
private static final Map<UUID, StuckContact> STUCK_ENTITIES = new HashMap<>();
public DuctTapeBlock(BlockBehaviour.Properties properties) {
super(properties);
@@ -138,14 +109,6 @@ public class DuctTapeBlock extends Block {
return state.canSurvive(context.getLevel(), context.getClickedPos()) ? state : null;
}
@Override
protected void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean movedByPiston) {
if (!state.is(newState.getBlock())) {
releaseEntitiesForTape(level, pos);
}
super.onRemove(state, level, pos, newState, movedByPiston);
}
@Override
protected ItemInteractionResult useItemOn(
ItemStack stack,
@@ -160,14 +123,6 @@ public class DuctTapeBlock extends Block {
return ItemInteractionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION;
}
if (isReleaseTool(stack) && (isStuckToTape(player) || isTouchingTape(level, player))) {
if (!level.isClientSide) {
consumeTapeUse(level, pos, state);
level.playSound(null, pos, SoundEvents.WOOL_BREAK, SoundSource.BLOCKS, 0.8F, 0.9F + level.random.nextFloat() * 0.2F);
}
return ItemInteractionResult.sidedSuccess(level.isClientSide);
}
if (!stack.isDamageableItem() || !stack.isDamaged() || stack.is(AntarchyTags.Items.DUCT_TAPE_BLACKLIST)) {
return ItemInteractionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION;
}
@@ -194,24 +149,6 @@ public class DuctTapeBlock extends Block {
return ItemInteractionResult.sidedSuccess(level.isClientSide);
}
@Override
protected void entityInside(BlockState state, Level level, BlockPos pos, Entity entity) {
if (level.isClientSide || entity.isSpectator() || !touchesTape(level, entity, pos, state)) {
return;
}
rememberStuckContact(level, pos, entity);
if (!(entity instanceof ItemEntity)) {
maybeConsumeTapeDecay(level, pos, state);
}
long gameTime = level.getGameTime();
String tapeKey = tapeKey(level, pos);
if (shouldPlayStickSound(entity.getUUID(), tapeKey, gameTime)) {
level.playSound(null, pos, AntarchySoundEvents.DUCT_TAPE_USE.get(), SoundSource.BLOCKS, 0.85F, 0.9F + level.random.nextFloat() * 0.15F);
}
applyStick(state, pos, entity);
}
@Override
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
builder.add(USES, FACE, FACING);
@@ -244,12 +181,6 @@ public class DuctTapeBlock extends Block {
};
}
private static boolean isReleaseTool(ItemStack stack) {
return stack.getItem() instanceof DiggerItem
|| stack.getItem() instanceof SwordItem
|| stack.getItem() instanceof ShearsItem;
}
private static void consumeTapeUse(Level level, BlockPos pos, BlockState state) {
int remainingUses = state.getValue(USES) - 1;
if (remainingUses > 0) {
@@ -257,322 +188,6 @@ public class DuctTapeBlock extends Block {
return;
}
releaseEntitiesForTape(level, pos);
TAPE_DECAY_TIMES.remove(tapeDecayKey(level, pos));
level.removeBlock(pos, false);
}
private static void removeTape(Level level, BlockPos pos, BlockState state) {
releaseEntitiesForTape(level, pos);
TAPE_DECAY_TIMES.remove(tapeDecayKey(level, pos));
level.removeBlock(pos, false);
}
private static void maybeConsumeTapeDecay(Level level, BlockPos pos, BlockState state) {
long gameTime = level.getGameTime();
String key = tapeDecayKey(level, pos);
Long lastConsumed = TAPE_DECAY_TIMES.get(key);
if (lastConsumed == null) {
TAPE_DECAY_TIMES.put(key, gameTime);
return;
}
if (gameTime - lastConsumed < TAPE_DECAY_INTERVAL_TICKS) {
return;
}
TAPE_DECAY_TIMES.put(key, gameTime);
consumeTapeUse(level, pos, state);
}
public static void tickStuckEntity(Entity entity) {
if (entity == null || entity.level().isClientSide() || isStickyBlacklist(entity)) {
return;
}
if (!entity.isAlive() || entity.isRemoved()) {
releaseEntity(entity);
return;
}
StuckContact contact = STUCK_ENTITIES.get(entity.getUUID());
if (contact != null && isContactValid(entity.level(), contact)) {
BlockPos pos = BlockPos.of(contact.blockPos());
BlockState state = entity.level().getBlockState(pos);
applyStick(state, pos, entity);
if (!(entity instanceof ItemEntity)) {
maybeConsumeTapeDecay(entity.level(), pos, state);
}
return;
}
if (contact != null) {
releaseEntity(entity);
}
if (!shouldSearchForTape(entity)) {
return;
}
TapeHit hit = findTouchedTape(entity.level(), entity);
if (hit == null) {
if (entity.isNoGravity()) {
entity.setNoGravity(false);
}
return;
}
rememberStuckContact(entity.level(), hit.pos(), entity);
applyStick(hit.state(), hit.pos(), entity);
if (!(entity instanceof ItemEntity)) {
maybeConsumeTapeDecay(entity.level(), hit.pos(), hit.state());
}
}
public static boolean isStuckToTape(Entity entity) {
if (entity == null || entity.level().isClientSide() || isStickyBlacklist(entity)) {
return false;
}
StuckContact contact = STUCK_ENTITIES.get(entity.getUUID());
return contact != null && isContactValid(entity.level(), contact);
}
public static boolean isTouchingTape(Entity entity) {
return entity != null && isTouchingTape(entity.level(), entity);
}
public static boolean isTouchingTape(Level level, Entity entity) {
if (isStickyBlacklist(entity)) {
return false;
}
return findTouchedTape(level, entity) != null;
}
public static boolean shouldTickStuckEntity(Entity entity) {
if (entity == null || entity.level().isClientSide() || isStickyBlacklist(entity)) {
return false;
}
if (!entity.isAlive() || entity.isRemoved()) {
return true;
}
StuckContact contact = STUCK_ENTITIES.get(entity.getUUID());
if (contact != null) {
return true;
}
return shouldSearchForTape(entity);
}
private static boolean isStickyBlacklist(Entity entity) {
return entity != null && entity.getType().is(AntarchyTags.Entities.DUCT_TAPE_STICK_BLACKLIST);
}
private static void applyStick(BlockState state, BlockPos pos, Entity entity) {
entity.setDeltaMovement(Vec3.ZERO);
entity.resetFallDistance();
entity.hasImpulse = true;
entity.setNoGravity(true);
Vec3 anchor = getTapeAnchor(state, pos, entity);
entity.setPos(anchor.x, anchor.y, anchor.z);
if (state.getValue(FACE) == AttachFace.FLOOR) {
entity.setOnGround(true);
}
}
private static void rememberStuckContact(Level level, BlockPos pos, Entity entity) {
UUID entityId = entity.getUUID();
String key = tapeKey(level, pos);
StuckContact existing = STUCK_ENTITIES.get(entityId);
if (existing != null && existing.tapeKey().equals(key)) {
return;
}
double offsetX = entity.getX() - pos.getX();
double offsetY = entity.getY() - pos.getY();
double offsetZ = entity.getZ() - pos.getZ();
STUCK_ENTITIES.put(entityId, new StuckContact(
key,
level.dimension().location().toString(),
pos.asLong(),
clamp(offsetX, 0.05D, 0.95D),
clamp(offsetY, 0.0D, 0.98D),
clamp(offsetZ, 0.05D, 0.95D)
));
}
private static Vec3 getTapeAnchor(BlockState state, BlockPos pos, Entity entity) {
StuckContact contact = STUCK_ENTITIES.get(entity.getUUID());
double offsetX = contact != null ? contact.offsetX() : 0.5D;
double offsetY = contact != null ? contact.offsetY() : 0.0D;
double offsetZ = contact != null ? contact.offsetZ() : 0.5D;
double wallOffset = entity instanceof Player ? 0.06D : -0.28D;
double centerX = pos.getX() + 0.5D;
double centerZ = pos.getZ() + 0.5D;
return switch (state.getValue(FACE)) {
case FLOOR -> new Vec3(
pos.getX() + offsetX,
pos.getY() + 0.01D,
pos.getZ() + offsetZ
);
case WALL -> switch (state.getValue(FACING)) {
case NORTH -> new Vec3(centerX, pos.getY() + offsetY, pos.getZ() - wallOffset);
case SOUTH -> new Vec3(centerX, pos.getY() + offsetY, pos.getZ() + 1.0D + wallOffset);
case EAST -> new Vec3(pos.getX() + 1.0D + wallOffset, pos.getY() + offsetY, centerZ);
case WEST -> new Vec3(pos.getX() - wallOffset, pos.getY() + offsetY, centerZ);
default -> new Vec3(centerX, pos.getY() + offsetY, centerZ);
};
default -> new Vec3(pos.getX() + offsetX, pos.getY() + 0.01D, pos.getZ() + offsetZ);
};
}
private static boolean isContactValid(Level level, StuckContact contact) {
if (!level.dimension().location().toString().equals(contact.dimensionId())) {
return false;
}
BlockPos pos = BlockPos.of(contact.blockPos());
BlockState state = level.getBlockState(pos);
return state.getBlock() instanceof DuctTapeBlock
&& tapeKey(level, pos).equals(contact.tapeKey());
}
private static boolean shouldSearchForTape(Entity entity) {
Vec3 velocity = entity.getDeltaMovement();
if (velocity.lengthSqr() < SEARCH_MOTION_THRESHOLD_SQR) {
return false;
}
return (entity.tickCount + entity.getId()) % SEARCH_INTERVAL_TICKS == 0;
}
private static TapeHit findTouchedTape(Level level, Entity entity) {
AABB box = entity.getBoundingBox().inflate(SEARCH_PAD_XZ, SEARCH_PAD_Y, SEARCH_PAD_XZ);
BlockPos min = BlockPos.containing(box.minX, box.minY, box.minZ);
BlockPos max = BlockPos.containing(box.maxX, box.maxY, box.maxZ);
if (!level.hasChunksAt(min, max)) {
return null;
}
BlockPos.MutableBlockPos cursor = new BlockPos.MutableBlockPos();
for (int x = min.getX(); x <= max.getX(); x++) {
for (int y = min.getY(); y <= max.getY(); y++) {
for (int z = min.getZ(); z <= max.getZ(); z++) {
cursor.set(x, y, z);
BlockState state = level.getBlockState(cursor);
if (state.isAir()) {
continue;
}
if (state.getBlock() instanceof DuctTapeBlock && touchesTape(level, entity, cursor, state)) {
return new TapeHit(cursor.immutable(), state);
}
}
}
}
return null;
}
private static boolean touchesTape(Level level, Entity entity, BlockPos pos, BlockState state) {
return entity.getBoundingBox().intersects(getStickyTriggerBounds(state, pos));
}
private static AABB getStickyTriggerBounds(BlockState state, BlockPos pos) {
AABB bounds = getShapeFor(state).bounds().move(pos);
return switch (state.getValue(FACE)) {
case FLOOR -> new AABB(
bounds.minX - TRIGGER_PAD,
pos.getY() - 0.02D,
bounds.minZ - TRIGGER_PAD,
bounds.maxX + TRIGGER_PAD,
pos.getY() + FLOOR_TRIGGER_HEIGHT,
bounds.maxZ + TRIGGER_PAD
);
case WALL -> {
yield switch (state.getValue(FACING)) {
case NORTH -> new AABB(bounds.minX - WALL_SIDE_PAD, bounds.minY - WALL_Y_PAD, bounds.minZ - WALL_TRIGGER_DEPTH, bounds.maxX + WALL_SIDE_PAD, bounds.maxY + WALL_Y_PAD, bounds.maxZ + WALL_INNER_INSET);
case SOUTH -> new AABB(bounds.minX - WALL_SIDE_PAD, bounds.minY - WALL_Y_PAD, bounds.minZ - WALL_INNER_INSET, bounds.maxX + WALL_SIDE_PAD, bounds.maxY + WALL_Y_PAD, bounds.maxZ + WALL_TRIGGER_DEPTH);
case EAST -> new AABB(bounds.minX - WALL_TRIGGER_DEPTH, bounds.minY - WALL_Y_PAD, bounds.minZ - WALL_SIDE_PAD, bounds.maxX + WALL_INNER_INSET, bounds.maxY + WALL_Y_PAD, bounds.maxZ + WALL_SIDE_PAD);
case WEST -> new AABB(bounds.minX - WALL_INNER_INSET, bounds.minY - WALL_Y_PAD, bounds.minZ - WALL_SIDE_PAD, bounds.maxX + WALL_TRIGGER_DEPTH, bounds.maxY + WALL_Y_PAD, bounds.maxZ + WALL_SIDE_PAD);
default -> bounds.inflate(TRIGGER_INFLATE_DEFAULT);
};
}
default -> bounds.inflate(TRIGGER_INFLATE_DEFAULT);
};
}
private static void releaseEntitiesForTape(Level level, BlockPos pos) {
String key = tapeKey(level, pos);
AABB searchBox = AABB.unitCubeFromLowerCorner(Vec3.atLowerCornerOf(pos)).inflate(2.0D);
STUCK_ENTITIES.entrySet().removeIf(entry -> {
StuckContact contact = entry.getValue();
if (!key.equals(contact.tapeKey())) {
return false;
}
level.getEntitiesOfClass(Entity.class, searchBox, candidate -> candidate.getUUID().equals(entry.getKey()))
.forEach(candidate -> candidate.setNoGravity(false));
STICK_SOUND_TIMES.remove(entry.getKey());
STICK_CONTACT_KEYS.remove(entry.getKey());
return true;
});
}
private static void releaseEntity(Entity entity) {
if (entity == null) {
return;
}
STUCK_ENTITIES.remove(entity.getUUID());
STICK_SOUND_TIMES.remove(entity.getUUID());
STICK_CONTACT_KEYS.remove(entity.getUUID());
entity.setNoGravity(false);
}
private static String tapeDecayKey(Level level, BlockPos pos) {
return level.dimension().location() + ":" + pos.asLong();
}
private static String tapeKey(Level level, BlockPos pos) {
return level.dimension().location() + ":" + pos.asLong();
}
private static boolean shouldPlayStickSound(UUID entityId, String tapeKey, long gameTime) {
String lastKey = STICK_CONTACT_KEYS.get(entityId);
Long lastContact = STICK_SOUND_TIMES.get(entityId);
if (lastKey == null || !tapeKey.equals(lastKey)) {
STICK_CONTACT_KEYS.put(entityId, tapeKey);
STICK_SOUND_TIMES.put(entityId, gameTime);
return true;
}
if (lastContact == null || gameTime - lastContact > STICK_SOUND_COOLDOWN_TICKS) {
STICK_SOUND_TIMES.put(entityId, gameTime);
return true;
}
return false;
}
private static double clamp(double value, double min, double max) {
return Math.max(min, Math.min(max, value));
}
private record StuckContact(
String tapeKey,
String dimensionId,
long blockPos,
double offsetX,
double offsetY,
double offsetZ
) {
}
private record TapeHit(BlockPos pos, BlockState state) {
}
}
@@ -26,8 +26,7 @@ public interface PortalGunPortalCellAccess {
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())
&& (!java.util.Objects.equals(existing.gunId(), record.gunId())
|| existing.side() != record.side())) {
return false;
}
@@ -63,7 +62,7 @@ public interface PortalGunPortalCellAccess {
if (record.ownerId() == null || record.portalId() == null) {
continue;
}
if (!PortalGunSavedData.isRegistered(level, record.ownerId(), record.gunId(), record.channelName(), record.side(), record.portalId())) {
if (!PortalGunSavedData.isRegistered(level, record.gunId(), record.side(), record.portalId())) {
this.portalGun$removeFaceRecord(record.face(), record.portalId());
continue;
}
@@ -2,6 +2,7 @@ package com.craisinlord.antarchy.content.block.entity;
import com.craisinlord.antarchy.content.portalgun.PortalGunPlacement;
import com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity;
import com.craisinlord.antarchy.content.portalgun.PortalGunVariant;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -14,9 +15,8 @@ import net.minecraft.nbt.Tag;
public record PortalGunPortalFaceRecord(
UUID ownerId,
String ownerIdentity,
UUID gunId,
String channelName,
PortalGunVariant variant,
UUID portalId,
UUID linkedPortalId,
PortalGunPortalEntity.PortalSide side,
@@ -43,21 +43,20 @@ public record PortalGunPortalFaceRecord(
}
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);
return new PortalGunPortalFaceRecord(this.ownerId, this.gunId, this.variant, 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);
return new PortalGunPortalFaceRecord(this.ownerId, this.gunId, this.variant, 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.putInt("Variant", this.variant.id());
tag.putUUID("PortalId", this.portalId);
if (this.linkedPortalId != null) {
tag.putUUID("LinkedPortalId", this.linkedPortalId);
@@ -105,9 +104,8 @@ public record PortalGunPortalFaceRecord(
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"),
PortalGunVariant.byId(tag.getInt("Variant")),
tag.getUUID("PortalId"),
tag.hasUUID("LinkedPortalId") ? tag.getUUID("LinkedPortalId") : null,
side,
@@ -4,6 +4,7 @@ import com.craisinlord.antarchy.Antarchy;
import com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity;
import com.craisinlord.antarchy.content.portalgun.PortalGunPlacement;
import com.craisinlord.antarchy.content.portalgun.PortalGunSavedData;
import com.craisinlord.antarchy.content.portalgun.PortalGunVariant;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.HashSet;
@@ -35,9 +36,8 @@ import net.minecraft.world.phys.Vec3;
public class PortalGunPortalMasterBlockEntity extends BlockEntity implements PortalGunPortalCellAccess {
private final Map<Direction, PortalGunPortalFaceRecord> faceRecords = new EnumMap<>(Direction.class);
private UUID ownerId;
private String ownerIdentity;
private UUID gunId;
private String channelName;
private PortalGunVariant variant = PortalGunVariant.DEFAULT;
private UUID portalId;
private PortalGunPortalEntity.PortalSide side = PortalGunPortalEntity.PortalSide.BLUE;
private Direction facing = Direction.NORTH;
@@ -57,8 +57,7 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity implements Por
public void configure(
UUID ownerId,
UUID gunId,
String ownerIdentity,
String channelName,
PortalGunVariant variant,
UUID portalId,
PortalGunPortalEntity.PortalSide side,
Direction facing,
@@ -71,9 +70,8 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity implements Por
int portalHeight
) {
this.ownerId = ownerId;
this.ownerIdentity = ownerIdentity;
this.gunId = gunId;
this.channelName = channelName;
this.variant = variant == null ? PortalGunVariant.DEFAULT : variant;
this.portalId = portalId;
this.side = side;
this.facing = facing;
@@ -84,7 +82,7 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity implements Por
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.portalGun$putFaceRecord(new PortalGunPortalFaceRecord(ownerId, gunId, this.variant, portalId, this.linkedPortalId, side, facing, upAxis, this.worldPosition, this.basePos, List.of(this.portalSpots), this.compensatedSpots, pairTime, portalWidth, portalHeight, true));
this.setChanged();
}
@@ -105,30 +103,8 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity implements Por
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 PortalGunVariant getVariant() {
return this.variant;
}
public PortalGunPortalEntity.PortalSide getSide() {
@@ -228,12 +204,6 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity implements Por
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)
@@ -282,7 +252,7 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity implements Por
if (this.portalId == null || this.ownerId == null) {
return null;
}
if (!PortalGunSavedData.isRegistered(level, this.ownerId, this.gunId, this.channelName, this.side, this.portalId)) {
if (!PortalGunSavedData.isRegistered(level, this.gunId, this.side, this.portalId)) {
return null;
}
if (level.getEntity(this.portalId) instanceof PortalGunPortalEntity portal && !portal.isRemoved()) {
@@ -300,18 +270,18 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity implements Por
@SuppressWarnings("unchecked")
PortalGunPortalEntity restored = new PortalGunPortalEntity((EntityType<? extends PortalGunPortalEntity>) rawType, level);
restored.setUUID(this.portalId);
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.configure(this.ownerId, this.gunId, this.variant, 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, this.ownerId, this.gunId, this.channelName, this.side, restored.getUUID());
PortalGunSavedData.setPortal(level, this.gunId, 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())) {
|| !PortalGunSavedData.isRegistered(level, record.gunId(), record.side(), record.portalId())) {
return null;
}
if (level.getEntity(record.portalId()) instanceof PortalGunPortalEntity portal && !portal.isRemoved()) {
@@ -325,13 +295,13 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity implements Por
@SuppressWarnings("unchecked")
PortalGunPortalEntity restored = new PortalGunPortalEntity((EntityType<? extends PortalGunPortalEntity>) rawType, level);
restored.setUUID(record.portalId());
restored.configure(record.ownerId(), record.gunId(), record.ownerIdentity(), record.channelName(), record.side(), placement);
restored.configure(record.ownerId(), record.gunId(), record.variant(), 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);
PortalGunSavedData.setPortal(level, record.gunId(), record.side(), restored.getUUID());
PortalGunPortalEntity linked = PortalGunSavedData.findLoadedPortal(level, record.gunId(), record.side() == PortalGunPortalEntity.PortalSide.BLUE ? PortalGunPortalEntity.PortalSide.ORANGE : PortalGunPortalEntity.PortalSide.BLUE);
if (linked != null && linked != restored && !linked.isRemoved()) {
restored.linkTo(linked);
linked.linkTo(restored);
@@ -345,9 +315,7 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity implements Por
}
PortalGunPortalEntity counterpart = PortalGunSavedData.findLoadedPortal(
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()) {
@@ -365,15 +333,10 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity implements Por
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);
}
tag.putInt("Variant", this.variant.id());
if (this.portalId != null) {
tag.putUUID("PortalId", this.portalId);
}
@@ -414,9 +377,8 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity implements Por
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.variant = PortalGunVariant.byId(tag.getInt("Variant"));
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;
@@ -443,7 +405,7 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity implements Por
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));
this.portalGun$putFaceRecord(new PortalGunPortalFaceRecord(this.ownerId, this.gunId, this.variant, 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));
}
}
@@ -0,0 +1,90 @@
package com.craisinlord.antarchy.content.client;
import com.craisinlord.antarchy.content.network.PortalGunTransitPayload;
import com.craisinlord.antarchy.content.gravity.AntarchyGravityApi;
import com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity;
import com.craisinlord.antarchy.content.portalgun.PortalGunTransformUtil;
import com.craisinlord.antarchy.content.portalgun.PortalGunTransitMath;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientPacketListener;
import net.minecraft.client.player.LocalPlayer;
import net.minecraft.network.protocol.common.ServerboundCustomPayloadPacket;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
public final class PortalGunClientTransit {
private static long cooldownUntil;
private PortalGunClientTransit() {
}
public static void afterLocalMove(LocalPlayer player, AABB startBox, Vec3 velocity) {
if (player.level() == null || !player.isAlive() || player.isPassenger() || player.isSpectator()) {
return;
}
long gameTime = player.level().getGameTime();
if (gameTime < cooldownUntil && cooldownUntil - gameTime <= PortalGunPortalEntity.TELEPORT_COOLDOWN_TICKS) {
return;
}
AABB currentBox = player.getBoundingBox();
if (velocity.lengthSqr() <= 1.0E-12D && startBox.getCenter().distanceToSqr(currentBox.getCenter()) <= 1.0E-10D) {
return;
}
ClientPacketListener connection = Minecraft.getInstance().getConnection();
if (connection == null) {
return;
}
Vec3 crossingVelocity = AntarchyGravityApi.isGravityInverted(player)
? PortalGunTransitMath.entityVelocityToWorld(player, velocity)
: velocity;
for (PortalGunPortalEntity portal : PortalGunPortalEntity.findPortalsNearBounds(player.level(), startBox.minmax(currentBox).expandTowards(crossingVelocity).inflate(1.0D))) {
PortalGunPortalEntity linked = portal.getLinkedPortal();
if (linked == null || !linked.isAlive()) {
continue;
}
if (portal.resolveCrossing(player, startBox, currentBox, crossingVelocity) == null) {
continue;
}
Vec3 look = player.getLookAngle();
connection.send(new ServerboundCustomPayloadPacket(new PortalGunTransitPayload(
portal.getId(), PortalGunTransitMath.entityPosition(startBox, player), player.position(), velocity)));
PortalGunPortalEntity.PortalTransit transit = portal.computeTransit(player, linked, currentBox, velocity, look);
Vec3 rollLook = AntarchyGravityApi.isGravityInverted(player)
? PortalGunTransitMath.entityVelocityToWorld(player, look)
: look;
PortalGunRollClientState.onTransit(portal, linked, rollLook);
apply(player, transit);
cooldownUntil = gameTime + PortalGunPortalEntity.TELEPORT_COOLDOWN_TICKS;
return;
}
}
private static void apply(LocalPlayer player, PortalGunPortalEntity.PortalTransit transit) {
Vec3 position = transit.position();
float yaw = PortalGunTransformUtil.yawFromLook(transit.look());
float pitch = PortalGunTransformUtil.pitchFromLook(transit.look());
player.setPos(position.x, position.y, position.z);
player.xo = position.x;
player.yo = position.y;
player.zo = position.z;
player.xOld = position.x;
player.yOld = position.y;
player.zOld = position.z;
player.setYRot(yaw);
player.setXRot(pitch);
player.yRotO = yaw;
player.xRotO = pitch;
player.setYHeadRot(yaw);
player.yHeadRotO = yaw;
player.setYBodyRot(yaw);
player.yBodyRotO = yaw;
player.setDeltaMovement(transit.velocity());
Vec3 fallVelocity = PortalGunTransitMath.entityVelocityToWorld(player, transit.velocity());
player.fallDistance = PortalGunTransitMath.exitFallDistance(player.fallDistance, fallVelocity, player);
player.setOnGround(false);
player.horizontalCollision = false;
player.verticalCollision = false;
player.verticalCollisionBelow = false;
player.setPortalCooldown(PortalGunPortalEntity.TELEPORT_COOLDOWN_TICKS);
}
}
@@ -0,0 +1,94 @@
package com.craisinlord.antarchy.content.client;
import com.craisinlord.antarchy.content.item.PortalGunItem;
import com.craisinlord.antarchy.content.network.PortalGunIndicatorPayload;
import com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity;
import com.mojang.blaze3d.systems.RenderSystem;
import java.util.EnumSet;
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.item.ItemStack;
public final class PortalGunCrosshairRenderer {
private static final int WIDTH = 34;
private static final int HEIGHT = 33;
private static final int HALF_WIDTH = 17;
private static final float MISSING_BRIGHTNESS = 0.3F;
private static final float MISSING_ALPHA = 0.75F;
private static ClientLevel cachedLevel;
private static long cachedGameTime = Long.MIN_VALUE;
private static UUID cachedGunId;
private static Set<PortalGunPortalEntity.PortalSide> cachedPortals = Set.of();
private PortalGunCrosshairRenderer() {
}
public static void render(GuiGraphics guiGraphics) {
Minecraft minecraft = Minecraft.getInstance();
if (minecraft.player == null || minecraft.level == 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;
}
}
ResourceLocation crosshair = ((PortalGunItem) held.getItem()).getVariant().crosshairTexture();
UUID gunId = PortalGunItem.getGunId(held);
PortalGunIndicatorClientState.requestIfNeeded(minecraft, gunId);
Set<PortalGunPortalEntity.PortalSide> portals = portalsFor(minecraft, gunId);
PortalGunIndicatorPayload serverState = PortalGunIndicatorClientState.get(gunId);
boolean blueAvailable = serverState == null
? portals.contains(PortalGunPortalEntity.PortalSide.BLUE)
: serverState.blueAvailable();
boolean orangeAvailable = serverState == null
? portals.contains(PortalGunPortalEntity.PortalSide.ORANGE)
: serverState.orangeAvailable();
int left = (minecraft.getWindow().getGuiScaledWidth() - WIDTH) / 2;
int top = (minecraft.getWindow().getGuiScaledHeight() - HEIGHT) / 2;
RenderSystem.enableBlend();
RenderSystem.defaultBlendFunc();
drawHalf(guiGraphics, crosshair, left + 6, top, 0.0F, blueAvailable);
drawHalf(guiGraphics, crosshair, left + 11, top, HALF_WIDTH, orangeAvailable);
guiGraphics.setColor(1.0F, 1.0F, 1.0F, 1.0F);
RenderSystem.disableBlend();
}
private static void drawHalf(GuiGraphics guiGraphics, ResourceLocation crosshair, int x, int y, float u, boolean available) {
if (available) {
guiGraphics.setColor(1.0F, 1.0F, 1.0F, 1.0F);
} else {
guiGraphics.setColor(MISSING_BRIGHTNESS, MISSING_BRIGHTNESS, MISSING_BRIGHTNESS, MISSING_ALPHA);
}
guiGraphics.blit(crosshair, x, y, u, 0.0F, HALF_WIDTH, HEIGHT, WIDTH, HEIGHT);
}
private static Set<PortalGunPortalEntity.PortalSide> portalsFor(Minecraft minecraft, UUID gunId) {
if (gunId == null) {
return Set.of();
}
ClientLevel level = minecraft.level;
long gameTime = level.getGameTime();
if (cachedLevel != level || cachedGameTime != gameTime || !gunId.equals(cachedGunId)) {
Set<PortalGunPortalEntity.PortalSide> portals = EnumSet.noneOf(PortalGunPortalEntity.PortalSide.class);
for (PortalGunPortalEntity portal : com.craisinlord.antarchy.content.portalgun.PortalGunPortalRegistry.all(level)) {
if (portal.isAlive() && gunId.equals(portal.getGunId())) {
portals.add(portal.getPortalSide());
}
}
cachedLevel = level;
cachedGameTime = gameTime;
cachedGunId = gunId;
cachedPortals = Set.copyOf(portals);
}
return cachedPortals;
}
}
@@ -1,20 +0,0 @@
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;
}
}
@@ -2,20 +2,14 @@ package com.craisinlord.antarchy.content.client;
import com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity;
import com.craisinlord.antarchy.content.portalgun.PortalGunTransformUtil;
import java.util.HashMap;
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 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<UUID, Long> TELEPORT_COOLDOWNS = new HashMap<>();
private static float previousRollDegrees;
private static float rollDegrees;
@@ -34,10 +28,7 @@ public final class PortalGunRollClientState {
if (minecraft.level == null || entity == null) {
return false;
}
for (PortalGunPortalEntity portal : minecraft.level.getEntitiesOfClass(
PortalGunPortalEntity.class,
entity.getBoundingBox().inflate(PORTAL_QUERY_RADIUS),
Entity::isAlive)) {
for (PortalGunPortalEntity portal : com.craisinlord.antarchy.content.portalgun.PortalGunPortalRegistry.near(minecraft.level, entity.getBoundingBox().inflate(PORTAL_QUERY_RADIUS))) {
if (portal.intersectsEntityBounds(entity)) {
return true;
}
@@ -53,7 +44,6 @@ public final class PortalGunRollClientState {
public static void clear() {
previousRollDegrees = 0.0F;
rollDegrees = 0.0F;
TELEPORT_COOLDOWNS.clear();
}
public static void tick(Minecraft minecraft) {
@@ -61,74 +51,17 @@ public final class PortalGunRollClientState {
clear();
return;
}
Entity cameraEntity = minecraft.getCameraEntity();
if (cameraEntity == null) {
return;
}
previousRollDegrees = rollDegrees;
rollDegrees *= ROLL_DECAY;
if (Math.abs(rollDegrees) < ROLL_EPSILON) {
rollDegrees = 0.0F;
}
long gameTime = minecraft.level.getGameTime();
TELEPORT_COOLDOWNS.entrySet().removeIf(entry -> entry.getValue() <= gameTime);
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;
}
if (!portal.shouldRenderFront(cameraEntity.position())) {
continue;
}
if (!canTeleport(cameraEntity, gameTime)) {
continue;
}
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, crossing, gameTime);
break;
}
}
private static boolean canTeleport(Entity entity, long gameTime) {
if (!entity.isAlive() || entity instanceof PortalGunPortalEntity || entity.isPassenger() || entity.isVehicle()) {
return false;
}
long cooldownUntil = TELEPORT_COOLDOWNS.getOrDefault(entity.getUUID(), 0L);
return cooldownUntil <= 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 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);
public static void onTransit(PortalGunPortalEntity sourcePortal, PortalGunPortalEntity destinationPortal, Vec3 look) {
Vec3 transformedLook = PortalGunTransformUtil.transformVector(sourcePortal, destinationPortal, look).normalize();
Vec3 transformedUp = PortalGunTransformUtil.transformVector(sourcePortal, destinationPortal, PortalGunTransformUtil.upVectorFromLookAndRoll(look, rollDegrees)).normalize();
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;
previousRollDegrees = rollDegrees;
}
}
@@ -6,6 +6,7 @@ import net.minecraft.client.Options;
import net.minecraft.world.item.ItemStack;
public final class PortalGunZoomClientState {
private static final int MAX_PROGRESS = 5;
private static int progress;
private static boolean zooming;
private static Double originalSensitivity;
@@ -34,7 +35,7 @@ public final class PortalGunZoomClientState {
zooming = false;
}
progress = Math.max(0, Math.min(5, progress + (zooming ? 1 : -1)));
progress = Math.max(0, Math.min(MAX_PROGRESS, progress + (zooming ? 1 : -1)));
if (originalSensitivity != null) {
if (progress == 0) {
restoreSensitivity(minecraft.options);
@@ -47,7 +48,13 @@ public final class PortalGunZoomClientState {
restoreSensitivity(options);
return fov;
}
double easedProgress = zooming ? progress - 1.0D + partialTick : progress + 1.0D - partialTick;
double tickProgress = Math.max(0.0D, Math.min(1.0D, partialTick));
double easedProgress = progress == MAX_PROGRESS
? MAX_PROGRESS
: zooming
? progress - 1.0D + tickProgress
: progress + 1.0D - tickProgress;
easedProgress = Math.max(0.0D, Math.min(MAX_PROGRESS, easedProgress));
double zoomFactor = zoomFactor(easedProgress);
if (originalSensitivity != null) {
options.sensitivity().set(originalSensitivity * zoomFactor);
@@ -56,7 +63,8 @@ public final class PortalGunZoomClientState {
}
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)))));
double normalizedProgress = Math.max(0.0D, Math.min(1.0D, progress / MAX_PROGRESS));
return 0.1D + 0.9D * (1.0D - Math.sin(Math.toRadians(90.0D * normalizedProgress)));
}
private static double zoomFactor(int progress) {
@@ -72,7 +72,7 @@ public final class ElythiaSkyRenderer {
float blue = (float) skyColor.z;
float rainLevel = level.getRainLevel(partialTick);
float starBrightness = level.getStarBrightness(partialTick) * (1.0F - rainLevel);
float timeOfDay = level.getTimeOfDay(partialTick);
double timeDegrees = (level.getDayTime() + partialTick) * (360.0D / 24000.0D);
// Blend in a dark purple when it's night (starBrightness > 0)
float nightPurpleR = 0.055F;
@@ -90,7 +90,7 @@ public final class ElythiaSkyRenderer {
RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F);
renderSunrise(poseStack, projectionMatrix, level, partialTick, rainLevel);
renderCelestialBodies(poseStack.last().pose(), projectionMatrix, timeOfDay, rainLevel);
renderCelestialBodies(poseStack.last().pose(), projectionMatrix, timeDegrees, rainLevel);
if (starBrightness > 0.1F) {
if (coloredStarBuffer == null) {
@@ -210,28 +210,31 @@ public final class ElythiaSkyRenderer {
RenderSystem.disableBlend();
}
private static void renderCelestialBodies(Matrix4f modelViewMatrix, Matrix4f projectionMatrix, float timeOfDay, float rainLevel) {
float t = timeOfDay * 360.0F;
private static void renderCelestialBodies(Matrix4f modelViewMatrix, Matrix4f projectionMatrix, double timeDegrees, float rainLevel) {
// Purple sun
float sun1Orbit = t;
float sun1Orbit = orbitAngle(timeDegrees, 1.0D, 0.0D);
renderBodyGlow(modelViewMatrix, projectionMatrix, sun1Orbit, 0.0F, 0xD06BFF, rainLevel, 70.0F);
renderTexturedBody(modelViewMatrix, projectionMatrix, sun1Orbit, 0.0F, 38.0F, 0xD06BFF, 1.0F, SUN_TEXTURE, false, 0, t * 0.8F);
renderTexturedBody(modelViewMatrix, projectionMatrix, sun1Orbit, 0.0F, 38.0F, 0xD06BFF, 1.0F, SUN_TEXTURE, false, 0, orbitAngle(timeDegrees, 0.8D, 0.0D));
// Orange sub
float sun2Orbit = t * 0.73F + 54.0F;
float sun2Orbit = orbitAngle(timeDegrees, 0.73D, 54.0D);
renderBodyGlow(modelViewMatrix, projectionMatrix, sun2Orbit, -14.0F, 0xFF9A33, rainLevel, 70.0F);
renderTexturedBody(modelViewMatrix, projectionMatrix, sun2Orbit, -14.0F, 16.0F, 0xFF9A33, 1.0F, SUN_TEXTURE, false, 0, -t * 0.5F);
renderTexturedBody(modelViewMatrix, projectionMatrix, sun2Orbit, -14.0F, 16.0F, 0xFF9A33, 1.0F, SUN_TEXTURE, false, 0, orbitAngle(timeDegrees, -0.5D, 0.0D));
// Dark green moon
float moon2Orbit = t * 1.27F + 228.0F;
float moon2Orbit = orbitAngle(timeDegrees, 1.27D, 228.0D);
renderBodyGlow(modelViewMatrix, projectionMatrix, moon2Orbit, 36.0F, 0x4FD84D, rainLevel, 78.0F);
renderTexturedBody(modelViewMatrix, projectionMatrix, moon2Orbit, 36.0F, 14.0F, 0x4FD84D, 1.0F, MOON_TEXTURE, true, 1, t * 0.3F);
renderTexturedBody(modelViewMatrix, projectionMatrix, moon2Orbit, 36.0F, 14.0F, 0x4FD84D, 1.0F, MOON_TEXTURE, true, 1, orbitAngle(timeDegrees, 0.3D, 0.0D));
// Light green moon
float moon3Orbit = t * 0.38F + 122.0F;
float moon3Orbit = orbitAngle(timeDegrees, 0.38D, 122.0D);
renderBodyGlow(modelViewMatrix, projectionMatrix, moon3Orbit, -34.0F, 0xA3FF8F, rainLevel, 78.0F);
renderTexturedBody(modelViewMatrix, projectionMatrix, moon3Orbit, -34.0F, 12.0F, 0xA3FF8F, 1.0F, MOON_TEXTURE, true, 2, -t * 0.6F);
renderTexturedBody(modelViewMatrix, projectionMatrix, moon3Orbit, -34.0F, 12.0F, 0xA3FF8F, 1.0F, MOON_TEXTURE, true, 2, orbitAngle(timeDegrees, -0.6D, 0.0D));
}
private static float orbitAngle(double timeDegrees, double speed, double offset) {
return (float) ((timeDegrees * speed + offset) % 360.0D);
}
private static void renderBodyGlow(
@@ -15,8 +15,6 @@ import net.minecraft.world.effect.MobEffect;
import net.minecraft.world.entity.LivingEntity;
public final class GlimmeringLivingLayer<T extends LivingEntity, M extends EntityModel<T>> extends RenderLayer<T, M> {
// High-alpha, luminance-preserving tint drawn straight over the skin (no scale-up) so it
// reads as the body itself recolored spirit-blue rather than a separate ghost shell.
private static final int GLIMMERING_TINT = 0x4266CCFF;
public GlimmeringLivingLayer(RenderLayerParent<T, M> renderer) {
@@ -41,25 +41,25 @@ public class PortalGunPortalRenderer extends GeoEntityRenderer<PortalGunPortalEn
putAnimatedPortalVertex(consumer, pose, entity, portalScale, frameV, halfWidth, halfHeight, 1.0F, 0.0F);
putAnimatedPortalVertex(consumer, pose, entity, portalScale, frameV, halfWidth, -halfHeight, 1.0F, 1.0F);
} else {
putPortalVertex(consumer, pose, entity, portalScale, -halfWidth, -halfHeight, 0.0F, 1.0F, false);
putPortalVertex(consumer, pose, entity, portalScale, -halfWidth, halfHeight, 0.0F, 0.0F, false);
putPortalVertex(consumer, pose, entity, portalScale, halfWidth, halfHeight, 1.0F, 0.0F, false);
putPortalVertex(consumer, pose, entity, portalScale, halfWidth, -halfHeight, 1.0F, 1.0F, false);
putPortalVertex(consumer, pose, entity, portalScale, -halfWidth, -halfHeight, 0.0F, 1.0F, true);
putPortalVertex(consumer, pose, entity, portalScale, -halfWidth, halfHeight, 0.0F, 0.0F, true);
putPortalVertex(consumer, pose, entity, portalScale, halfWidth, halfHeight, 1.0F, 0.0F, true);
putPortalVertex(consumer, pose, entity, portalScale, halfWidth, -halfHeight, 1.0F, 1.0F, true);
}
VertexConsumer edge = bufferSource.getBuffer(RenderType.entityTranslucent(this.getTextureLocation(entity)));
putPortalVertex(edge, pose, entity, portalScale, -halfWidth - 0.06F, -halfHeight - 0.07F, 0.0F, 1.0F, true);
putPortalVertex(edge, pose, entity, portalScale, -halfWidth - 0.06F, halfHeight + 0.07F, 0.0F, 0.0F, true);
putPortalVertex(edge, pose, entity, portalScale, halfWidth + 0.06F, halfHeight + 0.07F, 1.0F, 0.0F, true);
putPortalVertex(edge, pose, entity, portalScale, halfWidth + 0.06F, -halfHeight - 0.07F, 1.0F, 1.0F, true);
VertexConsumer edge = bufferSource.getBuffer(RenderType.entityTranslucent(PortalGunPortalRim.texture(entity)));
float rimWidth = halfWidth + PortalGunPortalRim.EXTRA_WIDTH;
float rimHeight = halfHeight + PortalGunPortalRim.EXTRA_HEIGHT;
putPortalVertex(edge, pose, entity, portalScale, -rimWidth, -rimHeight, 0.0F, 1.0F, true);
putPortalVertex(edge, pose, entity, portalScale, -rimWidth, rimHeight, 0.0F, 0.0F, true);
putPortalVertex(edge, pose, entity, portalScale, rimWidth, rimHeight, 1.0F, 0.0F, true);
putPortalVertex(edge, pose, entity, portalScale, rimWidth, -rimHeight, 1.0F, 1.0F, true);
poseStack.popPose();
}
private static ResourceLocation portalTexture(PortalGunPortalEntity entity) {
String color = entity.getPortalSide() == PortalGunPortalEntity.PortalSide.BLUE ? "blue" : "orange";
String path = isFancyPortalsEnabled()
? "textures/entity/portal_gun/portal_" + color + "_vortex.png"
: "textures/vfx/portal_gun_portal_" + color + ".png";
return ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, path);
return isFancyPortalsEnabled()
? entity.getVariant().vortexTexture(entity.getPortalSide())
: PortalGunPortalRim.texture(entity);
}
private static boolean isFancyPortalsEnabled() {
@@ -84,10 +84,11 @@ public class PortalGunPortalRenderer extends GeoEntityRenderer<PortalGunPortalEn
Vec3 position = entity.getWidthVec().normalize().scale(horizontal * portalScale)
.add(entity.getUpVec().normalize().scale(vertical * portalScale))
.add(entity.getNormalVec().normalize().scale(0.03125D));
int red = channelTint ? entity.getChannelRed() : 255;
int green = channelTint ? entity.getChannelGreen() : 255;
int blue = channelTint ? entity.getChannelBlue() : 255;
int alpha = channelTint ? 150 : 210;
int color = entity.getPortalColor();
int red = channelTint ? color >> 16 & 0xFF : 255;
int green = channelTint ? color >> 8 & 0xFF : 255;
int blue = channelTint ? color & 0xFF : 255;
int alpha = channelTint ? PortalGunPortalRim.ENTITY_ALPHA : 210;
consumer.addVertex(pose, (float) position.x, (float) position.y, (float) position.z)
.setColor(red, green, blue, alpha)
.setUv(u, v)
@@ -0,0 +1,178 @@
package com.craisinlord.antarchy.content.client.renderer;
import com.craisinlord.antarchy.Antarchy;
import com.craisinlord.antarchy.content.item.PortalGunItem;
import com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity;
import com.mojang.blaze3d.platform.GlStateManager;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.BufferBuilder;
import com.mojang.blaze3d.vertex.BufferUploader;
import com.mojang.blaze3d.vertex.DefaultVertexFormat;
import com.mojang.blaze3d.vertex.MeshData;
import com.mojang.blaze3d.vertex.Tesselator;
import com.mojang.blaze3d.vertex.VertexFormat;
import com.mojang.blaze3d.vertex.VertexSorting;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.GameRenderer;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.phys.Vec3;
import org.joml.Matrix4f;
import org.joml.Matrix4fStack;
import org.lwjgl.opengl.GL11;
public final class PortalGunPortalRim {
public static final float EXTRA_WIDTH = 0.0F;
public static final float EXTRA_HEIGHT = 0.0F;
public static final int ENTITY_ALPHA = 150;
private static final double OVERLAY_OFFSET = 0.05D;
private static final int HIDDEN_RIM_ALPHA = 170;
private static final ResourceLocation BLUE_TEXTURE = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "textures/vfx/portal_gun_portal_blue.png");
private static final ResourceLocation ORANGE_TEXTURE = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "textures/vfx/portal_gun_portal_orange.png");
private PortalGunPortalRim() {
}
public static ResourceLocation texture(PortalGunPortalEntity portal) {
return portal.getPortalSide() == PortalGunPortalEntity.PortalSide.BLUE ? BLUE_TEXTURE : ORANGE_TEXTURE;
}
static void drawOverView(PortalGunPortalEntity portal, Vec3 cameraPos, Matrix4f viewMatrix, Matrix4f projectionMatrix, float partialTick) {
withCameraMatrices(viewMatrix, projectionMatrix, () -> {
RenderSystem.disableDepthTest();
RenderSystem.depthMask(false);
RenderSystem.defaultBlendFunc();
drawRims(List.of(portal), cameraPos, partialTick, ENTITY_ALPHA);
});
}
static void drawHiddenOutlines(Minecraft minecraft, Vec3 cameraPos, Matrix4f viewMatrix, Matrix4f projectionMatrix, float partialTick) {
if (minecraft.level == null || minecraft.player == null) {
return;
}
List<PortalGunPortalEntity> ownedPortals = collectOwnedPortals(minecraft);
if (ownedPortals.isEmpty()) {
return;
}
withCameraMatrices(viewMatrix, projectionMatrix, () -> {
RenderSystem.enableDepthTest();
RenderSystem.depthMask(false);
RenderSystem.depthFunc(GL11.GL_GREATER);
RenderSystem.blendFunc(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE);
drawRims(ownedPortals, cameraPos, partialTick, HIDDEN_RIM_ALPHA);
});
}
private static void withCameraMatrices(Matrix4f viewMatrix, Matrix4f projectionMatrix, Runnable draw) {
Matrix4fStack modelViewStack = RenderSystem.getModelViewStack();
Matrix4f previousProjection = new Matrix4f(RenderSystem.getProjectionMatrix());
VertexSorting previousSorting = RenderSystem.getVertexSorting();
modelViewStack.pushMatrix();
try {
modelViewStack.set(viewMatrix);
RenderSystem.applyModelViewMatrix();
RenderSystem.setProjectionMatrix(new Matrix4f(projectionMatrix), VertexSorting.DISTANCE_TO_ORIGIN);
RenderSystem.setShader(GameRenderer::getPositionTexColorShader);
RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F);
RenderSystem.disableCull();
RenderSystem.enableBlend();
draw.run();
} finally {
RenderSystem.depthFunc(GL11.GL_LEQUAL);
RenderSystem.enableDepthTest();
RenderSystem.depthMask(true);
RenderSystem.defaultBlendFunc();
RenderSystem.disableBlend();
RenderSystem.enableCull();
RenderSystem.setProjectionMatrix(previousProjection, previousSorting);
modelViewStack.popMatrix();
RenderSystem.applyModelViewMatrix();
}
}
private static List<PortalGunPortalEntity> collectOwnedPortals(Minecraft minecraft) {
Set<UUID> owners = new HashSet<>();
Player player = minecraft.player;
owners.add(player.getUUID());
addGunOwner(owners, player.getMainHandItem(), player.getUUID());
addGunOwner(owners, player.getOffhandItem(), player.getUUID());
List<PortalGunPortalEntity> portals = new ArrayList<>();
for (PortalGunPortalEntity portal : com.craisinlord.antarchy.content.portalgun.PortalGunPortalRegistry.all(minecraft.level)) {
if (portal.isAlive() && owners.contains(portal.getOwnerId())) {
portals.add(portal);
}
}
return portals;
}
private static void addGunOwner(Set<UUID> owners, ItemStack stack, UUID fallback) {
if (stack.getItem() instanceof PortalGunItem) {
UUID owner = PortalGunItem.getPortalOwnerId(stack, fallback);
if (owner != null) {
owners.add(owner);
}
}
}
private static void drawRims(List<PortalGunPortalEntity> portals, Vec3 cameraPos, float partialTick, int alpha) {
drawRimsWithTexture(portals, cameraPos, partialTick, alpha, BLUE_TEXTURE, PortalGunPortalEntity.PortalSide.BLUE);
drawRimsWithTexture(portals, cameraPos, partialTick, alpha, ORANGE_TEXTURE, PortalGunPortalEntity.PortalSide.ORANGE);
}
private static void drawRimsWithTexture(List<PortalGunPortalEntity> portals, Vec3 cameraPos, float partialTick, int alpha,
ResourceLocation texture, PortalGunPortalEntity.PortalSide side) {
BufferBuilder buffer = null;
for (PortalGunPortalEntity portal : portals) {
if (portal.getPortalSide() != side) {
continue;
}
if (buffer == null) {
buffer = Tesselator.getInstance().begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_TEX_COLOR);
}
addRim(buffer, portal, cameraPos, partialTick, alpha);
}
if (buffer == null) {
return;
}
MeshData mesh = buffer.build();
if (mesh == null) {
return;
}
RenderSystem.setShaderTexture(0, texture);
BufferUploader.drawWithShader(mesh);
}
private static void addRim(BufferBuilder buffer, PortalGunPortalEntity portal, Vec3 cameraPos, float partialTick, int alpha) {
float scale = portal.getPortalVisualScale(partialTick);
if (scale <= 0.0F) {
return;
}
Vec3 right = portal.getWidthVec().normalize();
Vec3 up = portal.getUpVec().normalize();
Vec3 center = portal.position().add(portal.getNormalVec().normalize().scale(OVERLAY_OFFSET)).subtract(cameraPos);
double halfWidth = (portal.getPortalWidth() * 0.5D + EXTRA_WIDTH) * scale;
double halfHeight = (portal.getPortalHeight() * 0.5D + EXTRA_HEIGHT) * scale;
int color = portal.getPortalColor();
int red = color >> 16 & 0xFF;
int green = color >> 8 & 0xFF;
int blue = color & 0xFF;
addVertex(buffer, center, right, up, -halfWidth, -halfHeight, 0.0F, 1.0F, red, green, blue, alpha);
addVertex(buffer, center, right, up, -halfWidth, halfHeight, 0.0F, 0.0F, red, green, blue, alpha);
addVertex(buffer, center, right, up, halfWidth, halfHeight, 1.0F, 0.0F, red, green, blue, alpha);
addVertex(buffer, center, right, up, halfWidth, -halfHeight, 1.0F, 1.0F, red, green, blue, alpha);
}
private static void addVertex(BufferBuilder buffer, Vec3 center, Vec3 right, Vec3 up, double horizontal, double vertical,
float u, float v, int red, int green, int blue, int alpha) {
Vec3 position = center.add(right.scale(horizontal)).add(up.scale(vertical));
buffer.addVertex((float) position.x, (float) position.y, (float) position.z)
.setUv(u, v)
.setColor(red, green, blue, alpha);
}
}
@@ -53,7 +53,7 @@ public final class PortalGunPortalSceneRenderer {
public static void render(Minecraft minecraft, Camera camera, float partialTick, Matrix4f viewMatrix,
Matrix4f projection, Matrix4f skyProjection, Frustum frustum,
PortalTerrain terrain, Consumer<PoseStack> crossingEntities, String tracePair) {
PortalTerrain terrain, Consumer<PoseStack> crossingEntities) {
LevelRenderer renderer = minecraft.levelRenderer;
LevelRendererPortalViewAreaAccessor state = (LevelRendererPortalViewAreaAccessor) renderer;
LevelRendererPortalSceneInvoker scene = (LevelRendererPortalSceneInvoker) renderer;
@@ -82,7 +82,7 @@ public final class PortalGunPortalSceneRenderer {
boolean foggy = minecraft.level.effects().isFoggyAt(cameraBlockPos.getX(), cameraBlockPos.getY())
|| minecraft.gui.getBossOverlay().shouldCreateWorldFog();
try (PortalSceneRenderTrace.Phase ignored = PortalSceneRenderTrace.begin(tracePair, "background-and-sky", "sections=" + terrain.sectionCount())) {
{
RenderSystem.setShaderGameTime(minecraft.level.getGameTime(), partialTick);
minecraft.getBlockEntityRenderDispatcher().prepare(minecraft.level, camera, minecraft.hitResult);
minecraft.getEntityRenderDispatcher().prepare(minecraft.level, camera, minecraft.crosshairPickEntity);
@@ -99,7 +99,7 @@ public final class PortalGunPortalSceneRenderer {
RenderSystem.setProjectionMatrix(projection, VertexSorting.DISTANCE_TO_ORIGIN);
FogRenderer.setupFog(camera, FogRenderer.FogMode.FOG_TERRAIN, Math.max(renderDistance, 32.0F), foggy, partialTick);
try (PortalSceneRenderTrace.Phase ignored = PortalSceneRenderTrace.begin(tracePair, "opaque-terrain", "sections=" + terrain.sectionCount())) {
{
RenderSystem.enableDepthTest();
RenderSystem.depthMask(true);
terrain.drawLayer(minecraft, RenderType.solid(), cameraPosition, viewMatrix, projection);
@@ -118,7 +118,7 @@ public final class PortalGunPortalSceneRenderer {
PoseStack poseStack = new PoseStack();
MultiBufferSource.BufferSource buffers = bufferSource();
try {
try (PortalSceneRenderTrace.Phase ignored = PortalSceneRenderTrace.begin(tracePair, "entities", "rendering")) {
{
for (Entity entity : minecraft.level.entitiesForRendering()) {
if (!minecraft.getEntityRenderDispatcher().shouldRender(entity, frustum, cameraX, cameraY, cameraZ)
&& !entity.hasIndirectPassenger(minecraft.player)) {
@@ -138,20 +138,20 @@ public final class PortalGunPortalSceneRenderer {
}
buffers.endBatch();
try (PortalSceneRenderTrace.Phase ignored = PortalSceneRenderTrace.begin(tracePair, "block-entities", "rendering")) {
{
terrain.forEachBlockEntity(minecraft, frustum, blockEntity ->
renderBlockEntity(minecraft, blockEntity, cameraX, cameraY, cameraZ, partialTick, poseStack, buffers));
}
buffers.endBatch();
try (PortalSceneRenderTrace.Phase ignored = PortalSceneRenderTrace.begin(tracePair, "translucent-and-particles", "rendering")) {
{
terrain.drawLayer(minecraft, RenderType.translucent(), cameraPosition, viewMatrix, projection);
buffers.endBatch();
terrain.drawLayer(minecraft, RenderType.tripwire(), cameraPosition, viewMatrix, projection);
minecraft.particleEngine.render(minecraft.gameRenderer.lightTexture(), camera, partialTick);
}
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);
@@ -45,19 +45,17 @@ import org.joml.Vector4f;
import org.lwjgl.opengl.GL11;
public final class PortalGunPortalViewRenderer {
private static final int MAX_ROOT_VIEWS = 2;
private static final int MAX_VIEW_LEVELS = 2;
private static final int MAX_VIEWS_PER_FRAME = 4;
private static final double PORTAL_SEARCH_RADIUS = 128.0D;
private static final int PORTAL_TARGET_FILTER = 9729;
private static final double SURFACE_OFFSET = 0.04D;
private static final double EXIT_CLIP_OFFSET = 0.01D;
private static final int APERTURE_SEGMENTS = 32;
private static final double APERTURE_RIM_FRACTION = 0.92D;
private static TextureTarget offscreenTarget;
private static ShaderInstance portalViewShader;
private static boolean renderingPortalView;
private static boolean loggedRendererActivation;
private static boolean loggedMissingPortalViewShader;
private static String lastPortalCandidateState = "";
private static long lastRenderFailureTick = Long.MIN_VALUE;
private PortalGunPortalViewRenderer() {
@@ -65,7 +63,6 @@ public final class PortalGunPortalViewRenderer {
public static void setPortalViewShader(ShaderInstance shader) {
portalViewShader = shader;
Antarchy.LOGGER.info("Portal gun view shader registered");
}
public static void clearPortalViewShader() {
@@ -77,7 +74,6 @@ public final class PortalGunPortalViewRenderer {
}
public static void releaseTargets() {
PortalSceneRenderTrace.reset();
PortalGunPortalVisibilityQueries.clear();
PortalGunPortalSectionViews.clear();
if (offscreenTarget != null) {
@@ -87,18 +83,35 @@ public final class PortalGunPortalViewRenderer {
}
public static void render(Camera camera, Matrix4f poseMatrix, Matrix4f projectionMatrix, DeltaTracker tickCounter) {
if (!isEnabled() || renderingPortalView) {
if (renderingPortalView) {
return;
}
if (isEnabled()) {
renderViews(camera, poseMatrix, projectionMatrix, tickCounter);
}
renderHiddenOutlines(camera, poseMatrix, projectionMatrix, tickCounter);
}
private static void renderHiddenOutlines(Camera camera, Matrix4f poseMatrix, Matrix4f projectionMatrix, DeltaTracker tickCounter) {
Minecraft minecraft = Minecraft.getInstance();
if (minecraft.level == null || camera == null || poseMatrix == null || projectionMatrix == null || tickCounter == null) {
return;
}
try {
minecraft.getMainRenderTarget().bindWrite(true);
PortalGunPortalRim.drawHiddenOutlines(minecraft, camera.getPosition(), new Matrix4f(poseMatrix), new Matrix4f(projectionMatrix),
tickCounter.getGameTimeDeltaPartialTick(true));
} catch (RuntimeException exception) {
logRenderFailure(minecraft, exception);
}
}
private static void renderViews(Camera camera, Matrix4f poseMatrix, Matrix4f projectionMatrix, DeltaTracker tickCounter) {
PortalRenderWatchdog.frame();
Minecraft minecraft = Minecraft.getInstance();
if (minecraft.level == null || minecraft.gameRenderer == null || camera == null || poseMatrix == null || projectionMatrix == null || tickCounter == null) {
return;
}
if (!loggedRendererActivation) {
loggedRendererActivation = true;
Antarchy.LOGGER.info("Portal gun view renderer hook active (terrain: {})", SodiumCompat.isLoaded() ? "sodium" : "vanilla");
}
Entity cameraEntity = minecraft.getCameraEntity();
if (cameraEntity == null) {
return;
@@ -127,10 +140,10 @@ public final class PortalGunPortalViewRenderer {
}
}
candidates.sort((left, right) -> Integer.compare(right.clip().area(), left.clip().area()));
if (candidates.size() > MAX_ROOT_VIEWS) {
candidates = candidates.subList(0, MAX_ROOT_VIEWS);
int maxRootViews = AntarchySettings.portalGunMaxLiveViews();
if (candidates.size() > maxRootViews) {
candidates = candidates.subList(0, maxRootViews);
}
logCandidateState(portals, candidates);
if (candidates.isEmpty()) {
return;
}
@@ -147,7 +160,7 @@ public final class PortalGunPortalViewRenderer {
Vector3f rootLook = cameraToWorld.transformDirection(new Vector3f(0.0F, 0.0F, -1.0F)).normalize();
Vector3f rootUp = cameraToWorld.transformDirection(new Vector3f(0.0F, 1.0F, 0.0F)).normalize();
RenderContext rootContext = new RenderContext(cameraPos, new Vec3(rootLook), new Vec3(rootUp), rootViewMatrix);
ViewBudget budget = new ViewBudget(MAX_VIEWS_PER_FRAME);
ViewBudget budget = new ViewBudget(maxRootViews * 2);
PortalRenderWatchdog.portalViewDrawn();
renderingPortalView = true;
RenderSystem.backupProjectionMatrix();
@@ -202,7 +215,6 @@ public final class PortalGunPortalViewRenderer {
if (!PortalTerrain.canView(minecraft, exit.center())) {
return;
}
String pairKey = source.getUUID() + "->" + destination.getUUID();
boolean root = parentLevel == 0;
boolean visible = !root || PortalGunPortalVisibilityQueries.shouldRender(source.getUUID(), destination.getUUID());
RenderTarget target = minecraft.getMainRenderTarget();
@@ -224,14 +236,21 @@ public final class PortalGunPortalViewRenderer {
Matrix4f clippedProjection = applyPortalClipPlane(rootProjection, transformed, destination);
RenderSystem.enableScissor(clip.x(), clip.y(), clip.width(), clip.height());
renderScene(minecraft, cameraEntity, partialTick, transformed, rootProjection, clippedProjection,
source, destination, clip, target.width, target.height, parentLevel + 1, pairKey);
source, destination, clip, target.width, target.height, parentLevel + 1);
if (parentLevel + 1 < MAX_VIEW_LEVELS) {
renderNestedViews(minecraft, cameraEntity, partialTick, transformed, rootProjection, clippedProjection,
destination, parentLevel + 1, clip, budget, target.width, target.height);
}
} finally {
PortalStencilTarget.popAperture(parentLevel,
() -> restorePortalApertureDepth(context.cameraPos(), context.viewMatrix(), parentProjection, source, partialTick));
try {
if (visible) {
PortalStencilTarget.drawInside(parentLevel + 1);
PortalGunPortalRim.drawOverView(source, context.cameraPos(), context.viewMatrix(), parentProjection, partialTick);
}
} finally {
PortalStencilTarget.popAperture(parentLevel,
() -> restorePortalApertureDepth(context.cameraPos(), context.viewMatrix(), parentProjection, source, partialTick));
}
if (parentClip == null) {
RenderSystem.disableScissor();
} else {
@@ -275,7 +294,6 @@ public final class PortalGunPortalViewRenderer {
TextureTarget target = ensureOffscreenTarget(mainTarget.width, mainTarget.height);
RenderContext transformed = transformContext(context, source, destination);
Matrix4f clippedProjection = applyPortalClipPlane(rootProjection, transformed, destination);
String pairKey = source.getUUID() + "->" + destination.getUUID();
MinecraftMainRenderTargetAccessor targetAccessor = (MinecraftMainRenderTargetAccessor) minecraft;
targetAccessor.antarchy$setMainRenderTarget(target);
try {
@@ -285,7 +303,7 @@ public final class PortalGunPortalViewRenderer {
target.bindWrite(true);
RenderSystem.enableScissor(clip.x(), clip.y(), clip.width(), clip.height());
renderScene(minecraft, cameraEntity, partialTick, transformed, rootProjection, clippedProjection,
source, destination, clip, target.width, target.height, 1, pairKey);
source, destination, clip, target.width, target.height, 1);
} finally {
RenderSystem.disableScissor();
targetAccessor.antarchy$setMainRenderTarget(mainTarget);
@@ -298,7 +316,7 @@ public final class PortalGunPortalViewRenderer {
private static void renderScene(Minecraft minecraft, Entity cameraEntity, float partialTick, RenderContext transformed,
Matrix4f rootProjection, Matrix4f clippedProjection,
PortalGunPortalEntity source, PortalGunPortalEntity destination,
ScreenClip clip, int width, int height, int level, String pairKey) {
ScreenClip clip, int width, int height, int level) {
PortalGunWorldPortalShape exit = destination.getWorldPortalShape();
PortalCamera portalCamera = new PortalCamera();
portalCamera.setup(minecraft.level, cameraEntity, partialTick, transformed.cameraPos(), transformed.look(), transformed.up());
@@ -308,42 +326,26 @@ public final class PortalGunPortalViewRenderer {
source.getWorldPortalShape(), exit, transformed.cameraPos(), transformed.look(), transformed.up(),
transformed.viewMatrix(), clippedProjection, level, false, destination.getUUID()));
try {
PortalTerrain terrain;
try (PortalSceneRenderTrace.Phase ignored = PortalSceneRenderTrace.begin(pairKey, "section-visibility", "level=" + level)) {
terrain = PortalTerrain.prepare(minecraft, destination.getUUID(), exit.center(), exit.normal(), frustum);
}
PortalTerrain terrain = PortalTerrain.prepare(minecraft, destination.getUUID(), exit.center(), exit.normal(), frustum);
PortalGunPortalSceneRenderer.render(minecraft, portalCamera, partialTick, transformed.viewMatrix(), clippedProjection,
rootProjection, frustum, terrain,
poseStack -> renderCrossingEntities(minecraft, transformed.cameraPos(), partialTick, source, destination, poseStack),
pairKey);
poseStack -> renderCrossingEntities(minecraft, transformed.cameraPos(), partialTick, source, destination, poseStack));
} finally {
PortalGunPortalRenderState.popPortalView(false);
}
}
private static List<PortalGunPortalEntity> collectPortals(Minecraft minecraft, Vec3 cameraPos) {
List<PortalGunPortalEntity> portals = minecraft.level.getEntitiesOfClass(PortalGunPortalEntity.class,
new AABB(cameraPos, cameraPos).inflate(PORTAL_SEARCH_RADIUS), Entity::isAlive);
portals.removeIf(portal -> portal.distanceToSqr(cameraPos) > PORTAL_SEARCH_RADIUS * PORTAL_SEARCH_RADIUS);
List<PortalGunPortalEntity> portals = new ArrayList<>();
for (PortalGunPortalEntity portal : com.craisinlord.antarchy.content.portalgun.PortalGunPortalRegistry.all(minecraft.level)) {
if (portal.isAlive() && portal.distanceToSqr(cameraPos) <= PORTAL_SEARCH_RADIUS * PORTAL_SEARCH_RADIUS) {
portals.add(portal);
}
}
portals.sort((left, right) -> Double.compare(left.distanceToSqr(cameraPos), right.distanceToSqr(cameraPos)));
return portals;
}
private static void logCandidateState(List<PortalGunPortalEntity> portals, List<RootCandidate> candidates) {
String state = portals.size() + "|" + candidates.stream()
.map(candidate -> candidate.portal().getUUID() + "->" + candidate.linked().getUUID())
.sorted()
.toList();
if (state.equals(lastPortalCandidateState)) {
return;
}
lastPortalCandidateState = state;
if (!candidates.isEmpty()) {
PortalSceneRenderTrace.arm(state);
}
Antarchy.LOGGER.info("Portal gun view candidates changed portals={} selected={}", portals.size(), candidates.size());
}
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) {
@@ -382,8 +384,7 @@ public final class PortalGunPortalViewRenderer {
private static ScreenClip computeScreenClip(Vec3 cameraPos, Matrix4f viewMatrix, Matrix4f projectionMatrix,
PortalGunPortalEntity portal, float partialTick, int width, int height) {
List<Vector4f> clipVertices = projectPortalPolygon(new Matrix4f(projectionMatrix).mul(viewMatrix), cameraPos,
portal.getWorldPortalShape().scaleAperture(portal.getPortalVisualScale(partialTick)));
List<Vector4f> clipVertices = projectPortalPolygon(new Matrix4f(projectionMatrix).mul(viewMatrix), cameraPos, portal, partialTick);
if (clipVertices.size() < 3) {
return null;
}
@@ -456,8 +457,8 @@ public final class PortalGunPortalViewRenderer {
return nearPlane ? vertex.z + vertex.w : vertex.w - 0.001F;
}
private static List<Vector4f> projectPortalPolygon(Matrix4f combined, Vec3 cameraPos, PortalGunWorldPortalShape shape) {
Vec3[] corners = shape.getCorners(SURFACE_OFFSET);
private static List<Vector4f> projectPortalPolygon(Matrix4f combined, Vec3 cameraPos, PortalGunPortalEntity portal, float partialTick) {
Vec3[] corners = apertureOutline(portal, partialTick);
List<Vector4f> polygon = new ArrayList<>(corners.length);
for (Vec3 corner : corners) {
Vec3 relative = corner.subtract(cameraPos);
@@ -467,10 +468,17 @@ public final class PortalGunPortalViewRenderer {
return clipPolygon(polygon, true);
}
static Vec3[] apertureOutline(PortalGunPortalEntity portal, float partialTick) {
float scale = portal.getPortalVisualScale(partialTick);
PortalGunWorldPortalShape shape = portal.getWorldPortalShape().scaleAperture(scale);
double radiusHorizontal = Math.min(shape.halfWidth(), (shape.halfWidth() + PortalGunPortalRim.EXTRA_WIDTH * scale) * APERTURE_RIM_FRACTION);
double radiusVertical = Math.min(shape.halfHeight(), (shape.halfHeight() + PortalGunPortalRim.EXTRA_HEIGHT * scale) * APERTURE_RIM_FRACTION);
return shape.getOutline(SURFACE_OFFSET, APERTURE_SEGMENTS, radiusHorizontal, radiusVertical);
}
private static MeshData buildAperture(Vec3 cameraPos, Matrix4f viewMatrix, Matrix4f projectionMatrix,
PortalGunPortalEntity portal, float partialTick, boolean withScreenUv) {
PortalGunWorldPortalShape shape = portal.getWorldPortalShape().scaleAperture(portal.getPortalVisualScale(partialTick));
List<Vector4f> polygon = projectPortalPolygon(new Matrix4f(projectionMatrix).mul(viewMatrix), cameraPos, shape);
List<Vector4f> polygon = projectPortalPolygon(new Matrix4f(projectionMatrix).mul(viewMatrix), cameraPos, portal, partialTick);
if (polygon.size() < 3) {
return null;
}
@@ -22,7 +22,6 @@ public final class PortalGunPortalVisibilityQueries {
state.visible = GL15.glGetQueryObjecti(state.query, GL15.GL_QUERY_RESULT) != 0;
state.hasResult = true;
state.inFlight = false;
PortalSceneRenderTrace.event(source + "->" + destination, "visibility-query-result", "visible=" + state.visible);
}
return !state.hasResult || state.visible;
}
@@ -4,6 +4,7 @@ import com.craisinlord.antarchy.Antarchy;
import com.craisinlord.antarchy.content.portalgun.PortalGunProjectileEntity;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.entity.EntityRenderer;
@@ -11,11 +12,25 @@ import net.minecraft.client.renderer.entity.EntityRendererProvider;
import net.minecraft.client.renderer.texture.OverlayTexture;
import net.minecraft.core.BlockPos;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.HumanoidArm;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.phys.Vec3;
import org.joml.Matrix4f;
public class PortalGunProjectileRenderer extends EntityRenderer<PortalGunProjectileEntity> {
private static final ResourceLocation BLUE_TEXTURE = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "textures/vfx/portal_gun_portal_blue.png");
private static final ResourceLocation ORANGE_TEXTURE = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "textures/vfx/portal_gun_portal_orange.png");
private static final ResourceLocation SHOT_TEXTURE = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "textures/vfx/portal_gun_shot.png");
private static final float CORE_SIZE = 0.16F;
private static final float GLOW_SIZE = 0.34F;
private static final float STREAK_WIDTH = 0.045F;
private static final double STREAK_LENGTH = 2.2D;
private static final float MUZZLE_TICKS = 4.0F;
private static final double MUZZLE_FORWARD = 0.55D;
private static final double MUZZLE_SIDE = 0.32D;
private static final double MUZZLE_DOWN = 0.28D;
private static final double HIDDEN_DISTANCE = 0.9D;
private static final double FADE_DISTANCE = 1.8D;
public PortalGunProjectileRenderer(EntityRendererProvider.Context context) {
super(context);
@@ -23,37 +38,103 @@ public class PortalGunProjectileRenderer extends EntityRenderer<PortalGunProject
@Override
public void render(PortalGunProjectileEntity entity, float entityYaw, float partialTick, PoseStack poseStack, MultiBufferSource bufferSource, int packedLight) {
poseStack.pushPose();
poseStack.mulPose(this.entityRenderDispatcher.cameraOrientation());
float age = entity.tickCount + partialTick;
float syncedDistance = entity.getSyncedDistance() + partialTick;
float pulse = 0.8F + 0.2F * (float) Math.sin(age * 0.9F);
float travelPulse = 0.82F + 0.18F * (float) Math.sin(syncedDistance * 0.55F);
float trailStretch = 1.1F + (float) Math.min(entity.getSyncedVelocity().length() * 0.42D, 1.8D);
float baseScale = entity.getPortalSide() == com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity.PortalSide.BLUE ? 0.44F : 0.48F;
ResourceLocation texture = entity.getPortalSide() == com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity.PortalSide.BLUE ? BLUE_TEXTURE : ORANGE_TEXTURE;
float red = entity.getPortalSide() == com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity.PortalSide.BLUE ? 0.55F : 1.0F;
float green = entity.getPortalSide() == com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity.PortalSide.BLUE ? 0.82F : 0.56F;
float blue = entity.getPortalSide() == com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity.PortalSide.BLUE ? 1.0F : 0.18F;
Vec3 entityPos = entity.getPosition(partialTick);
Vec3 renderPos = this.muzzleBlendedPosition(entity, entityPos, partialTick);
Vec3 cameraPos = this.entityRenderDispatcher.camera.getPosition();
double cameraDistance = renderPos.distanceTo(cameraPos);
if (cameraDistance < HIDDEN_DISTANCE) {
return;
}
float fade = (float) Mth.clamp((cameraDistance - HIDDEN_DISTANCE) / (FADE_DISTANCE - HIDDEN_DISTANCE), 0.0D, 1.0D);
int color = entity.getVariant().color(entity.getPortalSide());
float red = (color >> 16 & 0xFF) / 255.0F;
float green = (color >> 8 & 0xFF) / 255.0F;
float blue = (color & 0xFF) / 255.0F;
poseStack.scale(baseScale * trailStretch, baseScale * travelPulse, baseScale * travelPulse);
poseStack.pushPose();
Vec3 offset = renderPos.subtract(entityPos);
poseStack.translate(offset.x, offset.y, offset.z);
this.renderStreak(entity, renderPos, cameraPos, poseStack, bufferSource, red, green, blue, fade, partialTick);
poseStack.mulPose(this.entityRenderDispatcher.cameraOrientation());
PoseStack.Pose pose = poseStack.last();
Matrix4f matrix = pose.pose();
VertexConsumer core = bufferSource.getBuffer(RenderType.entityTranslucent(texture));
addQuad(core, pose, matrix, red, green, blue, 0.92F, packedLight, -0.9F, -0.5F, 0.9F, 0.5F);
VertexConsumer glow = bufferSource.getBuffer(RenderType.entityTranslucentEmissive(texture));
addQuad(glow, pose, matrix, 1.0F, 1.0F, 1.0F, 0.8F * pulse, 0xF000F0, -1.15F, -0.7F, 1.15F, 0.7F);
poseStack.mulPose(com.mojang.math.Axis.ZP.rotationDegrees(90.0F));
pose = poseStack.last();
matrix = pose.pose();
core = bufferSource.getBuffer(RenderType.entityTranslucent(texture));
addQuad(core, pose, matrix, red, green, blue, 0.74F, packedLight, -0.45F, -0.95F, 0.45F, 0.95F);
glow = bufferSource.getBuffer(RenderType.entityTranslucentEmissive(texture));
addQuad(glow, pose, matrix, 1.0F, 1.0F, 1.0F, 0.52F * pulse, 0xF000F0, -0.62F, -1.15F, 0.62F, 1.15F);
VertexConsumer glow = bufferSource.getBuffer(RenderType.entityTranslucentEmissive(SHOT_TEXTURE));
addQuad(glow, pose, matrix, red, green, blue, 0.55F * fade, GLOW_SIZE);
addQuad(glow, pose, matrix, 1.0F, 1.0F, 1.0F, 0.9F * fade, CORE_SIZE);
poseStack.popPose();
}
private void renderStreak(PortalGunProjectileEntity entity, Vec3 renderPos, Vec3 cameraPos, PoseStack poseStack, MultiBufferSource bufferSource,
float red, float green, float blue, float fade, float partialTick) {
Vec3 velocity = entity.getSyncedVelocity();
if (velocity.lengthSqr() < 1.0E-6D) {
velocity = entity.getDeltaMovement();
}
if (velocity.lengthSqr() < 1.0E-6D) {
return;
}
Vec3 direction = velocity.normalize();
Vec3 toCamera = cameraPos.subtract(renderPos);
Vec3 side = direction.cross(toCamera);
if (side.lengthSqr() < 1.0E-6D) {
return;
}
side = side.normalize().scale(STREAK_WIDTH);
double travelled = entity.getSyncedSpawnPosition().distanceTo(renderPos);
double length = Math.min(STREAK_LENGTH, Math.max(0.0D, travelled - HIDDEN_DISTANCE));
if (length <= 0.05D) {
return;
}
Vec3 tail = direction.scale(-length);
Matrix4f matrix = poseStack.last().pose();
VertexConsumer streak = bufferSource.getBuffer(RenderType.lightning());
float headAlpha = 0.7F * fade;
Vec3 tailLeft = tail.add(side.scale(0.3D));
Vec3 tailRight = tail.subtract(side.scale(0.3D));
Vec3 headLeft = side;
Vec3 headRight = side.scale(-1.0D);
addStreakVertex(streak, matrix, headLeft, red, green, blue, headAlpha);
addStreakVertex(streak, matrix, tailLeft, red, green, blue, 0.0F);
addStreakVertex(streak, matrix, tailRight, red, green, blue, 0.0F);
addStreakVertex(streak, matrix, headRight, red, green, blue, headAlpha);
addStreakVertex(streak, matrix, headRight, red, green, blue, headAlpha);
addStreakVertex(streak, matrix, tailRight, red, green, blue, 0.0F);
addStreakVertex(streak, matrix, tailLeft, red, green, blue, 0.0F);
addStreakVertex(streak, matrix, headLeft, red, green, blue, headAlpha);
}
private static void addStreakVertex(VertexConsumer consumer, Matrix4f matrix, Vec3 position, float red, float green, float blue, float alpha) {
consumer.addVertex(matrix, (float) position.x, (float) position.y, (float) position.z).setColor(red, green, blue, alpha);
}
private Vec3 muzzleBlendedPosition(PortalGunProjectileEntity entity, Vec3 entityPos, float partialTick) {
float age = entity.tickCount + partialTick;
if (age >= MUZZLE_TICKS) {
return entityPos;
}
Entity shooter = Minecraft.getInstance().level == null ? null : Minecraft.getInstance().level.getEntity(entity.getShooterId());
if (!(shooter instanceof LivingEntity livingShooter)) {
return entityPos;
}
Vec3 eye = livingShooter.getEyePosition(partialTick);
Vec3 look = livingShooter.getViewVector(partialTick);
Vec3 right = look.cross(new Vec3(0.0D, 1.0D, 0.0D));
right = right.lengthSqr() < 1.0E-6D ? new Vec3(1.0D, 0.0D, 0.0D) : right.normalize();
Vec3 down = right.cross(look).normalize().scale(-1.0D);
boolean rightHanded = livingShooter.getMainArm() == HumanoidArm.RIGHT;
boolean offhand = !(livingShooter.getMainHandItem().getItem() instanceof com.craisinlord.antarchy.content.item.PortalGunItem)
&& livingShooter.getOffhandItem().getItem() instanceof com.craisinlord.antarchy.content.item.PortalGunItem;
double sideSign = rightHanded != offhand ? 1.0D : -1.0D;
Vec3 muzzle = eye.add(look.scale(MUZZLE_FORWARD)).add(right.scale(MUZZLE_SIDE * sideSign)).add(down.scale(MUZZLE_DOWN));
double blend = Mth.clamp(age / MUZZLE_TICKS, 0.0F, 1.0F);
return muzzle.lerp(entityPos, blend * blend);
}
@Override
public boolean shouldRender(PortalGunProjectileEntity entity, net.minecraft.client.renderer.culling.Frustum frustum, double cameraX, double cameraY, double cameraZ) {
return true;
}
@Override
protected int getBlockLightLevel(PortalGunProjectileEntity entity, BlockPos pos) {
return 15;
@@ -61,22 +142,22 @@ public class PortalGunProjectileRenderer extends EntityRenderer<PortalGunProject
@Override
public ResourceLocation getTextureLocation(PortalGunProjectileEntity entity) {
return entity.getPortalSide() == com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity.PortalSide.BLUE ? BLUE_TEXTURE : ORANGE_TEXTURE;
return SHOT_TEXTURE;
}
private static void addQuad(VertexConsumer consumer, PoseStack.Pose pose, Matrix4f matrix, float red, float green, float blue, float alpha, int packedLight, float minX, float minY, float maxX, float maxY) {
addVertex(consumer, pose, matrix, minX, minY, 0.0F, 1.0F, red, green, blue, alpha, packedLight);
addVertex(consumer, pose, matrix, maxX, minY, 1.0F, 1.0F, red, green, blue, alpha, packedLight);
addVertex(consumer, pose, matrix, maxX, maxY, 1.0F, 0.0F, red, green, blue, alpha, packedLight);
addVertex(consumer, pose, matrix, minX, maxY, 0.0F, 0.0F, red, green, blue, alpha, packedLight);
private static void addQuad(VertexConsumer consumer, PoseStack.Pose pose, Matrix4f matrix, float red, float green, float blue, float alpha, float halfSize) {
addVertex(consumer, pose, matrix, -halfSize, -halfSize, 0.0F, 1.0F, red, green, blue, alpha);
addVertex(consumer, pose, matrix, halfSize, -halfSize, 1.0F, 1.0F, red, green, blue, alpha);
addVertex(consumer, pose, matrix, halfSize, halfSize, 1.0F, 0.0F, red, green, blue, alpha);
addVertex(consumer, pose, matrix, -halfSize, halfSize, 0.0F, 0.0F, red, green, blue, alpha);
}
private static void addVertex(VertexConsumer consumer, PoseStack.Pose pose, Matrix4f matrix, float x, float y, float u, float v, float red, float green, float blue, float alpha, int packedLight) {
private static void addVertex(VertexConsumer consumer, PoseStack.Pose pose, Matrix4f matrix, float x, float y, float u, float v, float red, float green, float blue, float alpha) {
consumer.addVertex(matrix, x, y, 0.0F)
.setColor(red, green, blue, alpha)
.setUv(u, v)
.setOverlay(OverlayTexture.NO_OVERLAY)
.setLight(packedLight)
.setLight(0xF000F0)
.setNormal(pose, 0.0F, 0.0F, 1.0F);
}
}
@@ -1,91 +0,0 @@
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 String DEBUG_PROPERTY = "antarchy.portalGun.debugRender";
private static final long SLOW_PHASE_NANOS = 100_000_000L;
private static final Set<String> 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) {
boolean verbose = Boolean.getBoolean(DEBUG_PROPERTY);
long currentGeneration;
boolean logStart;
synchronized (PortalSceneRenderTrace.class) {
currentGeneration = generation;
logStart = LOGGED_PHASES.add(currentGeneration + ":" + pair + ":" + phase);
}
if (logStart) {
Antarchy.LOGGER.info("Portal scene trace begin generation={} pair={} phase={} detail={}", currentGeneration, pair, phase, detail);
}
return new Phase(currentGeneration, pair, phase, logStart, verbose, System.nanoTime());
}
public static void event(String pair, String event, String detail) {
if (!Boolean.getBoolean(DEBUG_PROPERTY)) {
return;
}
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 logStart;
private final boolean verbose;
private final long startNanos;
private boolean closed;
private Phase(long generation, String pair, String phase, boolean logStart, boolean verbose, long startNanos) {
this.generation = generation;
this.pair = pair;
this.phase = phase;
this.logStart = logStart;
this.verbose = verbose;
this.startNanos = startNanos;
}
@Override
public void close() {
if (this.closed) {
return;
}
this.closed = true;
long durationNanos = System.nanoTime() - this.startNanos;
if (this.logStart && (this.verbose || durationNanos >= SLOW_PHASE_NANOS)) {
Antarchy.LOGGER.info("Portal scene trace complete generation={} pair={} phase={} durationMs={}",
this.generation, this.pair, this.phase, durationNanos / 1_000_000.0D);
}
}
}
}
@@ -41,7 +41,7 @@ public final class PortalStencilTarget {
ensureMainStencilAttachment(target);
} catch (IllegalStateException exception) {
UNSUPPORTED_MAIN_TARGETS.put(target, target.frameBufferId);
Antarchy.LOGGER.warn("Portal gun direct stencil is unavailable; using offscreen portal rendering", exception);
Antarchy.LOGGER.debug("Portal gun direct stencil is unavailable ({}); using offscreen portal rendering", exception.getMessage());
}
}
}
@@ -150,7 +150,6 @@ public final class PortalStencilTarget {
throw new IllegalStateException("Main render target is incomplete with stencil attachment: 0x" + Integer.toHexString(status));
}
MAIN_STENCIL_ATTACHMENTS.put(target, new Attachment(target.frameBufferId, renderbuffer, target.width, target.height));
PortalSceneRenderTrace.event("main-target", "stencil-attached", "framebuffer=" + target.frameBufferId + " size=" + target.width + "x" + target.height);
} catch (RuntimeException | Error exception) {
GL30.glDeleteRenderbuffers(renderbuffer);
throw exception;
@@ -101,8 +101,6 @@ public final class RoyalIndicatorRenderer {
}
private static void judgmentFlare(VertexConsumer out, PoseStack.Pose pose, float radius, float rotation) {
// The old mark was almost edge-on from normal gameplay camera angles. Add a vertical,
// unmistakable judgment sigil while keeping the rotating ring as its core.
for (int i = 0; i < 4; i++) {
double angle = rotation + Math.PI * 0.5D * i;
float x = (float) Math.cos(angle) * radius;
@@ -4,7 +4,6 @@ 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;
@@ -427,6 +426,8 @@ public final class CreativeTabContents {
entry("growth_ray", "combat"),
entry("gravity_gun", "combat"),
entry("portal_gun", "combat"),
entry("portal_gun_atlas", "combat"),
entry("portal_gun_p_body", "combat"),
entry("eye_of_the_storm", "combat"),
entry("water_cannon", "combat"),
entry("attitude_adjuster", "combat"),
@@ -641,14 +642,6 @@ 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) {
@@ -678,14 +671,6 @@ 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));
@@ -115,7 +115,6 @@ public class HushProjectileEntity extends ThrowableItemProjectile {
return;
}
// Smoothly steer toward the target each tick
Vec3 desired = toTarget.normalize().scale(TRAVEL_SPEED);
this.setDeltaMovement(this.getDeltaMovement().lerp(desired, 0.1D));
}
@@ -167,7 +167,7 @@ public class MantisEntity extends Monster implements GeoEntity {
MantisEntity.class,
new AABB(pos).inflate(64.0D),
Entity::isAlive
).size() < 3;
).size() < 4;
}
@Override
@@ -284,9 +284,10 @@ public final class AntTeleportHelper {
// The bounded search can fail near rough or sparsely-generated terrain.
// Retry once with a wider radius before giving up.
loadArrivalSearchArea(destination, preferredPos, EXPANDED_ARRIVAL_SEARCH_RADIUS);
return findSafeArrivalPositionInYRange(
Vec3 expandedSafePos = findSafeArrivalPositionInYRange(
player, destination, preferredPos, yRange[0], yRange[1],
EXPANDED_ARRIVAL_SEARCH_RADIUS, EXPANDED_ARRIVAL_VERTICAL_SEARCH);
return expandedSafePos != null ? expandedSafePos : createEmergencyArrivalPlatform(destination, preferredPos, yRange[0], yRange[1]);
}
Set<BlockPos> candidates = new LinkedHashSet<>();
@@ -323,6 +324,46 @@ public final class AntTeleportHelper {
}
}
return createEmergencyArrivalPlatform(destination, preferredPos, destination.getMinBuildHeight() + 1, destination.getMaxBuildHeight() - 3);
}
@Nullable
private static Vec3 createEmergencyArrivalPlatform(ServerLevel destination, BlockPos preferredPos, int minY, int maxY) {
int y = Math.clamp(preferredPos.getY(), minY, maxY);
int centerX = preferredPos.getX();
int centerZ = preferredPos.getZ();
for (int radius = 0; radius <= EXPANDED_ARRIVAL_SEARCH_RADIUS; radius++) {
for (int xOff = -radius; xOff <= radius; xOff++) {
for (int zOff = -radius; zOff <= radius; zOff++) {
if (radius > 0 && Math.abs(xOff) != radius && Math.abs(zOff) != radius) continue;
int x = centerX + xOff;
int z = centerZ + zOff;
BlockPos floor = new BlockPos(x, y - 1, z);
boolean withinBorder = true;
for (int dx = -2; dx <= 2 && withinBorder; dx++) {
for (int dz = -2; dz <= 2; dz++) {
BlockPos check = floor.offset(dx, 0, dz);
if (!destination.getWorldBorder().isWithinBounds(check) || !destination.hasChunkAt(check.above(3))) {
withinBorder = false;
break;
}
}
}
if (!withinBorder) continue;
for (int dx = -2; dx <= 2; dx++) {
for (int dz = -2; dz <= 2; dz++) {
BlockPos pad = floor.offset(dx, 0, dz);
destination.setBlock(pad, Blocks.COBBLESTONE.defaultBlockState(), Block.UPDATE_ALL);
for (int dy = 1; dy <= 3; dy++) {
destination.setBlock(pad.above(dy), Blocks.AIR.defaultBlockState(), Block.UPDATE_ALL);
}
}
}
Vec3 safePos = new Vec3(x + 0.5D, y, z + 0.5D);
if (isValidArrivalPosition(destination, safePos)) return safePos;
}
}
}
return null;
}
@@ -141,9 +141,9 @@ public class BasiliskEntity extends Monster implements GeoEntity {
}
return level.getDifficulty() != Difficulty.PEACEFUL
&& pos.getY() >= 60 && pos.getY() <= 90
&& pos.getY() >= 60 && pos.getY() <= 100
&& level.getMaxLocalRawBrightness(pos) <= AntarchySettings.basiliskSpawnMaxLightLevel()
&& level.getEntitiesOfClass(BasiliskEntity.class, new AABB(pos).inflate(40.0D), Entity::isAlive).isEmpty()
&& level.getEntitiesOfClass(BasiliskEntity.class, new AABB(pos).inflate(36.0D), Entity::isAlive).isEmpty()
&& Monster.checkMonsterSpawnRules(entityType, level, spawnReason, pos, random);
}
@@ -277,12 +277,10 @@ public class BrutalflyEntity extends Monster implements GeoEntity {
this.cocoonHitCooldownTicks--;
}
// Pin position to hang from anchor
if (this.anchorPos != null) {
this.setPos(this.anchorPos.getX() + 0.5, this.anchorPos.getY() - 1.5, this.anchorPos.getZ() + 0.5);
this.setDeltaMovement(Vec3.ZERO);
// Hatch immediately if anchor log was broken
if (!this.isHatching && !this.level().getBlockState(this.anchorPos).is(BlockTags.LOGS)) {
this.startHatching();
return;
@@ -303,7 +301,6 @@ public class BrutalflyEntity extends Monster implements GeoEntity {
return;
}
// Count down hit animation
if (this.cocoonAnimTicks > 0) {
this.cocoonAnimTicks--;
if (this.cocoonAnimTicks <= 0) {
@@ -312,7 +309,6 @@ public class BrutalflyEntity extends Monster implements GeoEntity {
return;
}
// Occasionally play idle animation
if (this.getAnimationState() == ANIM_COCOON_IDLE) {
// Will be reset to COCOON after idle ticks counted down
} else if (this.tickCount % 200 == 17 && this.random.nextFloat() < 0.4F) {
@@ -7,12 +7,14 @@ 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.gravity.AntarchyGravityApi;
import com.craisinlord.antarchy.content.network.PortalGunPrimaryPayload;
import com.craisinlord.antarchy.content.portalgun.PortalGunBlackHoleEntity;
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.PortalGunVariant;
import com.craisinlord.antarchy.content.portalgun.PortalGunResetManager;
import com.craisinlord.antarchy.config.AntarchySettings;
import java.util.HashSet;
@@ -20,7 +22,6 @@ 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;
@@ -73,23 +74,19 @@ 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");
private final Supplier<? extends EntityType<? extends PortalGunPortalEntity>> portalType;
private final Supplier<? extends EntityType<? extends PortalGunBlackHoleEntity>> blackHoleType;
private final Supplier<? extends EntityType<? extends PortalGunProjectileEntity>> projectileType;
private final Supplier<? extends Block> portalMasterBlock;
private final Supplier<? extends Block> portalBaseBlock;
private final PortalGunVariant variant;
private final AnimatableInstanceCache geoCache = GeckoLibUtil.createInstanceCache(this);
public PortalGunItem(
@@ -98,9 +95,11 @@ public class PortalGunItem extends Item implements GeoItem {
Supplier<? extends EntityType<? extends PortalGunBlackHoleEntity>> blackHoleType,
Supplier<? extends EntityType<? extends PortalGunProjectileEntity>> projectileType,
Supplier<? extends Block> portalMasterBlock,
Supplier<? extends Block> portalBaseBlock
Supplier<? extends Block> portalBaseBlock,
PortalGunVariant variant
) {
super(properties);
this.variant = variant;
this.portalType = portalType;
this.blackHoleType = blackHoleType;
this.projectileType = projectileType;
@@ -109,6 +108,10 @@ public class PortalGunItem extends Item implements GeoItem {
GeoItem.registerSyncedAnimatable(this);
}
public PortalGunVariant getVariant() {
return this.variant;
}
@Override
public InteractionResultHolder<ItemStack> use(Level level, Player player, InteractionHand usedHand) {
ItemStack stack = player.getItemInHand(usedHand);
@@ -134,23 +137,21 @@ public class PortalGunItem extends Item implements GeoItem {
}
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));
UUID gunId = this.ensureGunId(stack, player.getUUID());
PortalGunSavedData.clearAllPortals(player.serverLevel(), gunId);
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);
UUID gunId = this.ensureGunId(stack, player.getUUID());
PortalGunSavedData.clearPortalSide(player.serverLevel(), gunId, 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());
this.ensureGunId(stack, shooter.getUUID());
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);
@@ -163,7 +164,7 @@ public class PortalGunItem extends Item implements GeoItem {
}
}
this.triggerFireAnimation(level, player, stack);
this.ensureGunId(stack, player.getUUID(), player.getGameProfile().getName());
this.ensureGunId(stack, player.getUUID());
stack.hurtAndBreak(1, player, offhand ? EquipmentSlot.OFFHAND : EquipmentSlot.MAINHAND);
player.awardStat(Stats.ITEM_USED.get(this));
player.getCooldowns().addCooldown(this, 6);
@@ -175,10 +176,8 @@ public class PortalGunItem extends Item implements GeoItem {
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);
PortalGunPortalEntity existing = this.findPlacedPortal(level, gunId, side);
PortalGunPlacement placement = this.findPlacement(level, shooter, gunId, side, hitResult, existing, portalWidth, portalHeight);
if (placement == null) {
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);
@@ -204,16 +203,16 @@ public class PortalGunItem extends Item implements GeoItem {
return;
}
PortalGunPortalEntity portal = new PortalGunPortalEntity(this.portalType.get(), level);
portal.configure(portalOwnerId, gunId, portalOwnerIdentity, channelName, side, placement);
portal.configure(portalOwnerId, gunId, this.variant, side, placement);
portal.moveTo(placement.center().x, placement.center().y, placement.center().z, placement.yaw(), 0.0F);
if (!this.placePortalBlocks(level, portalOwnerId, gunId, portalOwnerIdentity, channelName, side, portal, placement)) {
if (!this.placePortalBlocks(level, portalOwnerId, gunId, 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, portalOwnerId, gunId, channelName, side, portal.getUUID());
PortalGunPortalEntity other = this.findCounterpart(level, portalOwnerId, gunId, channelName, side);
PortalGunSavedData.setPortal(level, gunId, side, portal.getUUID());
PortalGunPortalEntity other = this.findCounterpart(level, gunId, side);
if (other != null) {
portal.linkTo(other);
other.linkTo(portal);
@@ -261,8 +260,7 @@ public class PortalGunItem extends Item implements GeoItem {
if (!level.dimensionType().hasSkyLight() || !level.isNight() || player.getXRot() > -40.0F) {
return false;
}
UUID ownerId = getPortalOwnerId(stack, player.getUUID());
PortalGunPortalEntity existing = this.findPlacedPortal(level, ownerId, this.ensureGunId(stack, player.getUUID(), player.getGameProfile().getName()), getChannelName(stack), side);
PortalGunPortalEntity existing = this.findPlacedPortal(level, this.ensureGunId(stack, player.getUUID()), side);
if (existing != null) {
existing.discard();
}
@@ -273,7 +271,7 @@ public class PortalGunItem extends Item implements GeoItem {
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"), 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);
this.spawnShotTrail(level, player, player.getEyePosition().add(this.worldLook(player).scale(64.0D)), side);
return true;
}
@@ -301,10 +299,10 @@ public class PortalGunItem extends Item implements GeoItem {
}
private void spawnProjectile(ServerLevel level, LivingEntity shooter, ItemStack stack, PortalGunPortalEntity.PortalSide side) {
UUID gunId = this.ensureGunId(stack, shooter.getUUID(), shooter.getName().getString());
UUID gunId = this.ensureGunId(stack, shooter.getUUID());
PortalGunProjectileEntity projectile = new PortalGunProjectileEntity(this.projectileType.get(), shooter, level);
projectile.configure(side, gunId, stack);
Vec3 look = shooter.getLookAngle();
Vec3 look = this.worldLook(shooter);
Vec3 start = shooter.getEyePosition();
double launchSpeed = PortalGunProjectileEntity.MIN_LAUNCH_SPEED + level.getRandom().nextDouble() * PortalGunProjectileEntity.LAUNCH_SPEED_VARIANCE;
projectile.setProjectileSpeed(launchSpeed);
@@ -324,14 +322,9 @@ public class PortalGunItem extends Item implements GeoItem {
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) {
private PortalGunPortalEntity findCounterpart(ServerLevel level, UUID gunId, PortalGunPortalEntity.PortalSide side) {
PortalGunPortalEntity.PortalSide otherSide = side == PortalGunPortalEntity.PortalSide.BLUE ? PortalGunPortalEntity.PortalSide.ORANGE : PortalGunPortalEntity.PortalSide.BLUE;
Optional<UUID> otherId = PortalGunSavedData.getPortalId(level.getServer(), owner, gunId, channelName, otherSide, level.dimension().location());
Optional<UUID> otherId = PortalGunSavedData.getPortalId(level.getServer(), gunId, otherSide, level.dimension().location());
if (otherId.isEmpty()) {
return null;
}
@@ -339,8 +332,8 @@ public class PortalGunItem extends Item implements GeoItem {
return entity instanceof PortalGunPortalEntity portal ? portal : null;
}
private PortalGunPortalEntity findPlacedPortal(ServerLevel level, UUID owner, UUID gunId, String channelName, PortalGunPortalEntity.PortalSide side) {
Optional<UUID> portalId = PortalGunSavedData.getPortalId(level.getServer(), owner, gunId, channelName, side, level.dimension().location());
private PortalGunPortalEntity findPlacedPortal(ServerLevel level, UUID gunId, PortalGunPortalEntity.PortalSide side) {
Optional<UUID> portalId = PortalGunSavedData.getPortalId(level.getServer(), gunId, side, level.dimension().location());
if (portalId.isEmpty()) {
return null;
}
@@ -354,11 +347,19 @@ public class PortalGunItem extends Item implements GeoItem {
private BlockHitResult raycast(Level level, Player player) {
Vec3 start = player.getEyePosition();
Vec3 end = start.add(player.getLookAngle().scale(64.0D));
Vec3 end = start.add(this.worldLook(player).scale(64.0D));
return level.clip(new ClipContext(start, end, ClipContext.Block.COLLIDER, ClipContext.Fluid.NONE, player));
}
private PortalGunPlacement findPlacement(Level level, LivingEntity shooter, UUID ownerId, String channelName, PortalGunPortalEntity.PortalSide side, BlockHitResult hitResult, PortalGunPortalEntity replacingPortal, int requestedWidth, int requestedHeight) {
private Vec3 worldLook(LivingEntity entity) {
Vec3 look = entity.getLookAngle();
return AntarchyGravityApi.isGravityInverted(entity)
? com.craisinlord.antarchy.content.gravity.AntarchyGravityRotationUtil.vecPlayerToWorld(
look, AntarchyGravityApi.getGravityDirection(entity))
: look;
}
private PortalGunPlacement findPlacement(Level level, LivingEntity shooter, UUID gunId, 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();
@@ -367,7 +368,7 @@ public class PortalGunItem extends Item implements GeoItem {
if (!canResize && (width != maxWidth || height != maxHeight)) {
continue;
}
PortalGunPlacement placement = this.findPlacementForSize(level, shooter, ownerId, channelName, side, hitResult, replacingPortal, width, height);
PortalGunPlacement placement = this.findPlacementForSize(level, shooter, gunId, side, hitResult, replacingPortal, width, height);
if (placement != null) {
return placement;
}
@@ -376,7 +377,7 @@ public class PortalGunItem extends Item implements GeoItem {
return null;
}
private PortalGunPlacement findPlacementForSize(Level level, LivingEntity shooter, UUID ownerId, String channelName, PortalGunPortalEntity.PortalSide side, BlockHitResult hitResult, PortalGunPortalEntity replacingPortal, int width, int height) {
private PortalGunPlacement findPlacementForSize(Level level, LivingEntity shooter, UUID gunId, PortalGunPortalEntity.PortalSide side, BlockHitResult hitResult, PortalGunPortalEntity replacingPortal, int width, int height) {
Direction facing = hitResult.getDirection();
Direction heightAxis = this.resolveUpAxis(shooter, facing);
Direction widthAxis = PortalGunPlacement.widthAxis(facing, heightAxis);
@@ -410,7 +411,7 @@ public class PortalGunItem extends Item implements GeoItem {
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)) {
|| !this.isPortalSpaceAvailable(level, portalPos, portalState, gunId, side, facing)) {
valid = false;
break;
}
@@ -505,13 +506,13 @@ public class PortalGunItem extends Item implements GeoItem {
return horizontal;
}
private boolean isPortalSpaceAvailable(Level level, BlockPos pos, BlockState state, UUID ownerId, String channelName, PortalGunPortalEntity.PortalSide side, Direction face) {
private boolean isPortalSpaceAvailable(Level level, BlockPos pos, BlockState state, UUID gunId, PortalGunPortalEntity.PortalSide side, Direction face) {
BlockEntity blockEntity = level.getBlockEntity(pos);
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;
return gunId != null && gunId.equals(master.getGunId()) && master.getSide() == side;
}
if (blockEntity instanceof PortalGunPortalBaseBlockEntity base && base.getPortalId() != null) {
BlockEntity masterEntity = level.getBlockEntity(base.getMasterPos());
@@ -519,23 +520,21 @@ public class PortalGunItem extends Item implements GeoItem {
return false;
}
return master.getFacing() != face
|| (ownerId != null
&& ownerId.equals(base.getOwnerId())
&& channelName.equals(master.getChannelName())
|| (gunId != null
&& gunId.equals(master.getGunId())
&& base.getSide() == side
&& master.getSide() == side);
}
return true;
}
return ownerId != null
&& ownerId.equals(record.ownerId())
&& channelName.equals(record.channelName())
return gunId != null
&& gunId.equals(record.gunId())
&& record.side() == side;
}
return state.getCollisionShape(level, pos).isEmpty();
}
private boolean placePortalBlocks(ServerLevel level, UUID ownerId, UUID gunId, String ownerIdentity, String channelName, PortalGunPortalEntity.PortalSide side, PortalGunPortalEntity portal, PortalGunPlacement placement) {
private boolean placePortalBlocks(ServerLevel level, UUID ownerId, UUID gunId, 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];
@@ -557,9 +556,8 @@ public class PortalGunItem extends Item implements GeoItem {
if (!(existing instanceof PortalGunPortalCellAccess cell)
|| !cell.portalGun$putFaceRecord(new PortalGunPortalFaceRecord(
ownerId,
ownerIdentity,
gunId,
channelName,
this.variant,
portal.getUUID(),
portal.getLinkedPortalId(),
side,
@@ -578,7 +576,7 @@ public class PortalGunItem extends Item implements GeoItem {
}
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());
master.configure(ownerId, gunId, this.variant, 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());
@@ -656,12 +654,8 @@ public class PortalGunItem extends Item implements GeoItem {
} else {
gunId = UUID.randomUUID();
}
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());
}
@@ -669,30 +663,16 @@ public class PortalGunItem extends Item implements GeoItem {
return gunId;
}
private UUID ensureGunId(ItemStack stack, UUID ownerId, String ownerName) {
private UUID ensureGunId(ItemStack stack, UUID ownerId) {
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;
@@ -704,7 +684,7 @@ public class PortalGunItem extends Item implements GeoItem {
if (stack.isEmpty() || !(stack.getItem() instanceof PortalGunItem item)) {
return null;
}
return item.ensureGunId(stack, owner.getUUID(), owner.getGameProfile().getName());
return item.ensureGunId(stack, owner.getUUID());
}
public static UUID getPortalOwnerId(ItemStack stack, UUID fallback) {
@@ -713,9 +693,6 @@ public class PortalGunItem extends Item implements GeoItem {
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) {
@@ -723,32 +700,6 @@ public class PortalGunItem extends Item implements GeoItem {
}
}
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;
@@ -767,21 +718,16 @@ public class PortalGunItem extends Item implements GeoItem {
return UseAnim.NONE;
}
private Component portalColorName(PortalGunPortalEntity.PortalSide side) {
return Component.translatable(this.variant.colorTranslationKey(side))
.withStyle(style -> style.withColor(net.minecraft.network.chat.TextColor.fromRgb(this.variant.color(side))));
}
@Override
public void appendHoverText(ItemStack stack, Item.TooltipContext context, List<Component> 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.primary", this.portalColorName(PortalGunPortalEntity.PortalSide.BLUE)).withStyle(ChatFormatting.GRAY));
tooltipComponents.add(Component.translatable("tooltip.antarchy.portal_gun.secondary", this.portalColorName(PortalGunPortalEntity.PortalSide.ORANGE)).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
@@ -809,7 +755,7 @@ public class PortalGunItem extends Item implements GeoItem {
@Override
public net.minecraft.client.renderer.BlockEntityWithoutLevelRenderer getGeoItemRenderer() {
if (this.renderer == null) {
this.renderer = new AnimatedHeldItemRenderer<>(new ResourceBackedGeoItemModel<>(MODEL_LOCATION, TEXTURE_LOCATION, ANIMATION_LOCATION));
this.renderer = new AnimatedHeldItemRenderer<>(new ResourceBackedGeoItemModel<>(MODEL_LOCATION, PortalGunItem.this.variant.modelTexture(), ANIMATION_LOCATION));
}
return this.renderer;
}
@@ -1,17 +0,0 @@
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<PortalGunGrabPayload> TYPE = new Type<>(ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "portal_gun_grab"));
public static final StreamCodec<ByteBuf, PortalGunGrabPayload> STREAM_CODEC = StreamCodec.unit(new PortalGunGrabPayload());
@Override
public Type<PortalGunGrabPayload> type() {
return TYPE;
}
}
@@ -1,20 +0,0 @@
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<PortalGunGrabStatePayload> TYPE = new Type<>(ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "portal_gun_grab_state"));
public static final StreamCodec<ByteBuf, PortalGunGrabStatePayload> STREAM_CODEC = StreamCodec.composite(
ByteBufCodecs.BOOL, PortalGunGrabStatePayload::active, PortalGunGrabStatePayload::new
);
@Override
public Type<PortalGunGrabStatePayload> type() {
return TYPE;
}
}
@@ -0,0 +1,43 @@
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;
import net.minecraft.world.phys.Vec3;
public record PortalGunTransitPayload(int portalId, Vec3 start, Vec3 end, Vec3 velocity) implements CustomPacketPayload {
public static final Type<PortalGunTransitPayload> TYPE = new Type<>(ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "portal_gun_transit"));
public static final StreamCodec<ByteBuf, PortalGunTransitPayload> STREAM_CODEC = StreamCodec.of(PortalGunTransitPayload::write, PortalGunTransitPayload::read);
private static void write(ByteBuf buffer, PortalGunTransitPayload payload) {
buffer.writeInt(payload.portalId());
writeVec(buffer, payload.start());
writeVec(buffer, payload.end());
writeVec(buffer, payload.velocity());
}
private static PortalGunTransitPayload read(ByteBuf buffer) {
int portalId = buffer.readInt();
Vec3 start = readVec(buffer);
Vec3 end = readVec(buffer);
Vec3 velocity = readVec(buffer);
return new PortalGunTransitPayload(portalId, start, end, velocity);
}
private static void writeVec(ByteBuf buffer, Vec3 vector) {
buffer.writeDouble(vector.x);
buffer.writeDouble(vector.y);
buffer.writeDouble(vector.z);
}
private static Vec3 readVec(ByteBuf buffer) {
return new Vec3(buffer.readDouble(), buffer.readDouble(), buffer.readDouble());
}
@Override
public Type<PortalGunTransitPayload> type() {
return TYPE;
}
}
@@ -43,9 +43,6 @@ public final class PortalGunCollisionHelper {
return collided;
}
PortalGunWorldPortalShape shape = portal.getWorldPortalShape();
if (canWalkIntoFloorPortal(startBox, movement, portal, shape)) {
return movement;
}
Vec3 normal = portal.getNormalVec().normalize();
double desiredNormal = movement.dot(normal);
double collidedNormal = collided.dot(normal);
@@ -60,29 +57,6 @@ public final class PortalGunCollisionHelper {
return applyBorderCollision(entity, startBox, adjusted, portal);
}
private static boolean canWalkIntoFloorPortal(AABB startBox, Vec3 movement, PortalGunPortalEntity portal, PortalGunWorldPortalShape shape) {
Vec3 normal = portal.getNormalVec().normalize();
if (Math.abs(normal.y) < 0.9D) {
return false;
}
AABB endBox = startBox.move(movement);
return overlapsPortalOpeningFromSurface(startBox, shape) || overlapsPortalOpeningFromSurface(endBox, shape);
}
private static boolean overlapsPortalOpeningFromSurface(AABB box, PortalGunWorldPortalShape shape) {
if (!shape.intersectsPortalColumn(box, EDGE_PADDING, EDGE_PADDING, PLANE_TOLERANCE)) {
return false;
}
double minDepth = Double.POSITIVE_INFINITY;
double maxDepth = Double.NEGATIVE_INFINITY;
for (Vec3 corner : corners(box)) {
double depth = shape.localCoords(corner).depth();
minDepth = Math.min(minDepth, depth);
maxDepth = Math.max(maxDepth, depth);
}
return maxDepth > 0.0D && minDepth >= -PLANE_TOLERANCE;
}
private static boolean canUsePortalPassThrough(Entity entity, AABB startBox, Vec3 movement, PortalGunPortalEntity portal, PortalGunWorldPortalShape shape) {
AABB endBox = startBox.move(movement);
if (!portal.getPortalInsides(entity).intersects(startBox)
@@ -1,280 +0,0 @@
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<UUID, GrabState> GRABS = new HashMap<>();
private static BiConsumer<ServerPlayer, PortalGunGrabStatePayload> stateSender = (player, payload) -> {};
private PortalGunGrabManager() {
}
public static void setStateSender(BiConsumer<ServerPlayer, PortalGunGrabStatePayload> 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<UUID, GrabState> 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<UUID, GrabState> 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) {
}
}
@@ -27,20 +27,15 @@ public final class PortalGunIndicatorSync {
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();
@@ -1,22 +1,26 @@
package com.craisinlord.antarchy.content.portalgun;
import com.craisinlord.antarchy.Antarchy;
import com.craisinlord.antarchy.config.AntarchySettings;
import com.craisinlord.antarchy.content.gravity.AntarchyGravityApi;
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 com.craisinlord.antarchy.mixins.ServerGamePacketListenerTeleportAccessor;
import com.craisinlord.antarchy.mixins.gravity.ChunkMapAccessor;
import com.craisinlord.antarchy.mixins.gravity.ServerEntityAccessor;
import com.craisinlord.antarchy.mixins.gravity.TrackedEntityAccessor;
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;
@@ -40,11 +44,13 @@ 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.projectile.Projectile;
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;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.Vec3;
import software.bernie.geckolib.animatable.GeoEntity;
import software.bernie.geckolib.animatable.instance.AnimatableInstanceCache;
@@ -64,14 +70,11 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity {
private static final EntityDataAccessor<Integer> SIDE = SynchedEntityData.defineId(PortalGunPortalEntity.class, EntityDataSerializers.INT);
private static final EntityDataAccessor<Optional<UUID>> OWNER_ID = SynchedEntityData.defineId(PortalGunPortalEntity.class, EntityDataSerializers.OPTIONAL_UUID);
private static final EntityDataAccessor<Optional<UUID>> GUN_ID = SynchedEntityData.defineId(PortalGunPortalEntity.class, EntityDataSerializers.OPTIONAL_UUID);
private static final EntityDataAccessor<String> CHANNEL_NAME = SynchedEntityData.defineId(PortalGunPortalEntity.class, EntityDataSerializers.STRING);
private static final EntityDataAccessor<Integer> FACING = SynchedEntityData.defineId(PortalGunPortalEntity.class, EntityDataSerializers.INT);
private static final EntityDataAccessor<Integer> UP_AXIS = SynchedEntityData.defineId(PortalGunPortalEntity.class, EntityDataSerializers.INT);
private static final EntityDataAccessor<Optional<UUID>> LINKED_PORTAL = SynchedEntityData.defineId(PortalGunPortalEntity.class, EntityDataSerializers.OPTIONAL_UUID);
private static final EntityDataAccessor<Integer> LINKED_PORTAL_ENTITY_ID = SynchedEntityData.defineId(PortalGunPortalEntity.class, EntityDataSerializers.INT);
private static final EntityDataAccessor<Integer> CHANNEL_RED = SynchedEntityData.defineId(PortalGunPortalEntity.class, EntityDataSerializers.INT);
private static final EntityDataAccessor<Integer> CHANNEL_GREEN = SynchedEntityData.defineId(PortalGunPortalEntity.class, EntityDataSerializers.INT);
private static final EntityDataAccessor<Integer> CHANNEL_BLUE = SynchedEntityData.defineId(PortalGunPortalEntity.class, EntityDataSerializers.INT);
private static final EntityDataAccessor<Integer> VARIANT = SynchedEntityData.defineId(PortalGunPortalEntity.class, EntityDataSerializers.INT);
private static final EntityDataAccessor<Integer> PORTAL_WIDTH = SynchedEntityData.defineId(PortalGunPortalEntity.class, EntityDataSerializers.INT);
private static final EntityDataAccessor<Integer> PORTAL_HEIGHT = SynchedEntityData.defineId(PortalGunPortalEntity.class, EntityDataSerializers.INT);
private static final EntityDataAccessor<Integer> OPENING_ANIMATION_ID = SynchedEntityData.defineId(PortalGunPortalEntity.class, EntityDataSerializers.INT);
@@ -81,14 +84,14 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity {
public static final int TELEPORT_COOLDOWN_TICKS = 3;
private static final String DEBUG_TRAVERSAL_PROPERTY = "antarchy.portalGun.debugTraversal";
private static final double PORTAL_INSIDE_SHIFT = 0.05D;
private static final Set<String> LOGGED_CROSSING_PATHS = new HashSet<>();
private static final double HALF_DEPTH = 0.35D;
private static final double MAX_CLIENT_TRANSIT_START_ERROR_SQR = 1.0D;
private static final double MAX_CLIENT_TRANSIT_MOVEMENT_SQR = 100.0D;
private static final double PROJECTILE_PLANE_TOLERANCE = 0.1D;
private final AnimatableInstanceCache geoCache = GeckoLibUtil.createInstanceCache(this);
private final Map<UUID, Integer> teleportCooldowns = new HashMap<>();
private UUID ownerId;
private String ownerIdentity;
private UUID gunId;
private String channelName;
private UUID linkedPortalId;
private int ageTicks;
private int lastOpeningAnimationId;
@@ -98,6 +101,12 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity {
private BlockPos basePos = BlockPos.ZERO;
private BlockPos[] portalSpots = new BlockPos[] {BlockPos.ZERO, BlockPos.ZERO};
private Set<BlockPos> compensatedSpots = Set.of();
private PortalGunWorldPortalShape cachedShape;
private Vec3 cachedShapePosition;
private int cachedShapeFacing;
private int cachedShapeUpAxis;
private int cachedShapeWidth;
private int cachedShapeHeight;
public PortalGunPortalEntity(EntityType<? extends PortalGunPortalEntity> entityType, Level level) {
super(entityType, level);
@@ -111,36 +120,27 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity {
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(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(VARIANT, PortalGunVariant.DEFAULT.id());
builder.define(PORTAL_WIDTH, 1);
builder.define(PORTAL_HEIGHT, 2);
builder.define(OPENING_ANIMATION_ID, 0);
}
public void configure(UUID ownerId, UUID gunId, String ownerIdentity, String channelName, PortalSide side, PortalGunPlacement placement) {
public void configure(UUID ownerId, UUID gunId, PortalGunVariant variant, 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(VARIANT, (variant == null ? PortalGunVariant.DEFAULT : variant).id());
this.entityData.set(SIDE, side.ordinal());
this.entityData.set(FACING, placement.facing().get3DDataValue());
this.entityData.set(UP_AXIS, placement.upAxis().get3DDataValue());
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();
@@ -191,50 +191,12 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity {
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 PortalGunVariant getVariant() {
return PortalGunVariant.byId(this.entityData.get(VARIANT));
}
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() {
return this.entityData.get(CHANNEL_RED);
}
public int getChannelGreen() {
return this.entityData.get(CHANNEL_GREEN);
}
public int getChannelBlue() {
return this.entityData.get(CHANNEL_BLUE);
public int getPortalColor() {
return this.getVariant().color(this.getPortalSide());
}
public BlockPos getBasePos() {
@@ -278,17 +240,10 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity {
if (linkedId == null) {
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);
return serverLevel.getEntity(linkedId) instanceof PortalGunPortalEntity portal && !portal.isRemoved() ? portal : null;
}
return linkedEntity instanceof PortalGunPortalEntity portal && !portal.isRemoved() ? portal : null;
return PortalGunPortalRegistry.find(this.level(), linkedId);
}
public Vec3 getNormalVec() {
@@ -304,7 +259,23 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity {
}
public PortalGunWorldPortalShape getWorldPortalShape() {
return new PortalGunWorldPortalShape(this.position(), this.getNormalVec().normalize(), this.getUpVec().normalize(), this.getWidthVec().normalize(), this.getPortalWidth() / 2.0D, this.getPortalHeight() / 2.0D, HALF_DEPTH);
Vec3 position = this.position();
int facing = this.entityData.get(FACING);
int upAxis = this.entityData.get(UP_AXIS);
int width = this.getPortalWidth();
int height = this.getPortalHeight();
PortalGunWorldPortalShape shape = this.cachedShape;
if (shape == null || !position.equals(this.cachedShapePosition) || facing != this.cachedShapeFacing
|| upAxis != this.cachedShapeUpAxis || width != this.cachedShapeWidth || height != this.cachedShapeHeight) {
shape = new PortalGunWorldPortalShape(position, this.getNormalVec().normalize(), this.getUpVec().normalize(), this.getWidthVec().normalize(), width / 2.0D, height / 2.0D, HALF_DEPTH);
this.cachedShape = shape;
this.cachedShapePosition = position;
this.cachedShapeFacing = facing;
this.cachedShapeUpAxis = upAxis;
this.cachedShapeWidth = width;
this.cachedShapeHeight = height;
}
return shape;
}
public AABB getPlane() {
@@ -354,72 +325,66 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity {
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);
if (entity instanceof Player) {
PortalGunWorldPortalShape shape = this.getWorldPortalShape();
Vec3 center = currentBox.getCenter();
PortalGunWorldPortalShape.PortalLocalCoords localCenter = shape.localCoords(center);
double previousDepth = shape.localCoords(previousBox.getCenter()).depth();
double currentDepth = localCenter.depth();
double halfX = (currentBox.maxX - currentBox.minX) * 0.5D;
double halfY = (currentBox.maxY - currentBox.minY) * 0.5D;
double halfZ = (currentBox.maxZ - currentBox.minZ) * 0.5D;
double depthExtent = halfX * Math.abs(shape.normal().x) + halfY * Math.abs(shape.normal().y) + halfZ * Math.abs(shape.normal().z);
if (previousDepth <= 0.0D || currentDepth >= previousDepth - 1.0E-6D
|| currentDepth - depthExtent > 0.02D || currentDepth < -shape.halfDepth()) {
return null;
}
double widthExtent = halfX * Math.abs(shape.right().x) + halfY * Math.abs(shape.right().y) + halfZ * Math.abs(shape.right().z);
double heightExtent = halfX * Math.abs(shape.up().x) + halfY * Math.abs(shape.up().y) + halfZ * Math.abs(shape.up().z);
if (Math.abs(localCenter.horizontal()) + widthExtent > shape.halfWidth() + 1.0E-3D
|| Math.abs(localCenter.vertical()) + heightExtent > shape.halfHeight() + 1.0E-3D) {
return null;
}
return new PortalCrossing(currentProbe, Math.max(0.05D, shape.localCoords(currentProbe).depth() + 0.05D));
public PortalCrossing resolveCrossing(Entity entity, AABB previousBox, AABB currentBox, Vec3 attemptedMovement) {
PortalGunWorldPortalShape shape = this.getWorldPortalShape();
if (!PortalGunTransitMath.crossesWithBody(shape, previousBox, currentBox, attemptedMovement)) {
return null;
}
return this.crossesPortal(previousProbe, currentProbe) ? new PortalCrossing(currentProbe, 0.0D) : null;
return new PortalCrossing(this.teleportProbePosition(entity, currentBox));
}
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());
PortalCrossing crossing = this.resolveCrossing(entity, previousBox, currentBox);
return crossing == null ? null : crossing.probe();
public PortalTransit computeTransit(PortalGunPortalEntity destination, AABB box, Vec3 velocity, Vec3 look) {
PortalGunWorldPortalShape source = this.getWorldPortalShape();
PortalGunWorldPortalShape target = destination.getWorldPortalShape();
AABB exitBox = PortalGunTransitMath.exitBox(source, target, box);
Vec3 exitVelocity = PortalGunTransitMath.exitVelocity(source, target, velocity,
AntarchySettings.portalGunMinFloorExitSpeed(), AntarchySettings.portalGunMaxExitSpeed());
Vec3 exitLook = PortalGunTransitMath.transformVector(source, target, look).normalize();
return new PortalTransit(exitBox, PortalGunTransitMath.feetPosition(exitBox), exitVelocity, exitLook);
}
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 PortalTransit computeTransit(Entity entity, PortalGunPortalEntity destination, AABB box, Vec3 velocity, Vec3 look) {
if (!AntarchyGravityApi.isGravityInverted(entity)) {
return computeTransit(destination, box, velocity, look);
}
PortalGunWorldPortalShape source = this.getWorldPortalShape();
PortalGunWorldPortalShape target = destination.getWorldPortalShape();
AABB exitBox = PortalGunTransitMath.exitBox(source, target, box);
Vec3 worldVelocity = PortalGunTransitMath.entityVelocityToWorld(entity, velocity);
Vec3 worldExitVelocity = PortalGunTransitMath.exitVelocity(source, target, worldVelocity,
AntarchySettings.portalGunMinFloorExitSpeed(), AntarchySettings.portalGunMaxExitSpeed());
Vec3 exitVelocity = PortalGunTransitMath.entityVelocityFromWorld(entity, worldExitVelocity);
Vec3 worldLook = PortalGunTransitMath.entityVelocityToWorld(entity, look);
Vec3 worldExitLook = PortalGunTransitMath.transformVector(source, target, worldLook).normalize();
Vec3 exitLook = PortalGunTransitMath.entityVelocityFromWorld(entity, worldExitLook);
return new PortalTransit(exitBox, PortalGunTransitMath.entityPosition(exitBox, entity), exitVelocity, exitLook);
}
public static void teleportEntityAfterMovement(Entity entity, AABB previousBox, Vec3 resolvedMovement) {
if (entity instanceof ServerPlayer) {
public static void teleportEntityAfterMovement(Entity entity, AABB previousBox, Vec3 velocity) {
if (entity instanceof Player) {
return;
}
teleportAfterMovement(entity, previousBox, entity.getBoundingBox(), resolvedMovement);
teleportAfterMovement(entity, previousBox, entity.getBoundingBox(), velocity);
}
public static void teleportPlayerAfterMovementPacket(ServerPlayer player, AABB previousBox, Vec3 claimedMovement) {
teleportAfterMovement(player, previousBox, previousBox.move(claimedMovement), claimedMovement);
Vec3 transitVelocity = PortalGunTransitMath.entityVelocityFromWorld(player, claimedMovement);
teleportAfterMovement(player, previousBox, previousBox.move(claimedMovement), transitVelocity, claimedMovement);
}
private static void teleportAfterMovement(Entity entity, AABB previousBox, AABB currentBox, Vec3 resolvedMovement) {
private static void teleportAfterMovement(Entity entity, AABB previousBox, AABB currentBox, Vec3 velocity) {
teleportAfterMovement(entity, previousBox, currentBox, velocity,
PortalGunTransitMath.entityVelocityToWorld(entity, velocity));
}
private static void teleportAfterMovement(Entity entity, AABB previousBox, AABB currentBox, Vec3 velocity, Vec3 attemptedMovement) {
if (entity.level().isClientSide || !entity.isAlive() || entity.isPassenger() || entity instanceof PortalGunPortalEntity) {
return;
}
if (previousBox.getCenter().distanceToSqr(currentBox.getCenter()) <= 1.0E-10D) {
if (velocity.lengthSqr() <= 1.0E-12D && previousBox.getCenter().distanceToSqr(currentBox.getCenter()) <= 1.0E-10D) {
return;
}
AABB sweptBounds = previousBox.minmax(currentBox).inflate(1.0D);
AABB sweptBounds = previousBox.minmax(currentBox).expandTowards(attemptedMovement).inflate(1.0D);
for (PortalGunPortalEntity portal : findPortalsNearBounds(entity.level(), sweptBounds)) {
if (!(portal.level() instanceof ServerLevel serverLevel) || !portal.canTeleportEntity(entity)) {
continue;
@@ -428,28 +393,132 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity {
if (linked == null || !linked.isAlive()) {
continue;
}
PortalCrossing crossing = portal.resolveCrossing(entity, previousBox, currentBox);
PortalCrossing crossing = portal.resolveCrossing(entity, previousBox, currentBox, attemptedMovement);
if (crossing != null) {
if (entity instanceof Player && Boolean.getBoolean(DEBUG_TRAVERSAL_PROPERTY)) {
Antarchy.LOGGER.info("Portal gun traversal accepted path=movement portal={} player={} probe={} offset={} linked={}",
portal.getUUID(), entity.getUUID(), crossing.probe(), crossing.normalOffset(), linked.getUUID());
Antarchy.LOGGER.info("Portal gun traversal accepted path=server portal={} player={} probe={} linked={}",
portal.getUUID(), entity.getUUID(), crossing.probe(), linked.getUUID());
}
logCrossingPath(entity, "movement");
portal.teleportEntity(entity, linked, crossing.probe(), resolvedMovement, crossing.normalOffset());
portal.teleportEntity(entity, linked, currentBox, velocity, false);
return;
}
}
}
public static boolean handleClientTransit(ServerPlayer player, int portalId, Vec3 start, Vec3 end, Vec3 velocity) {
if (!player.isAlive() || player.isPassenger() || player.isSpectator()) {
return false;
}
if (((ServerGamePacketListenerTeleportAccessor) player.connection).antarchy$getAwaitingPositionFromClient() != null) {
return true;
}
if (!isFinite(start) || !isFinite(end) || !isFinite(velocity)
|| start.distanceToSqr(player.position()) > MAX_CLIENT_TRANSIT_START_ERROR_SQR
|| end.distanceToSqr(start) > MAX_CLIENT_TRANSIT_MOVEMENT_SQR
|| velocity.lengthSqr() > MAX_CLIENT_TRANSIT_MOVEMENT_SQR) {
return false;
}
if (!(player.level() instanceof ServerLevel serverLevel)
|| !(serverLevel.getEntity(portalId) instanceof PortalGunPortalEntity portal)
|| !portal.isAlive()) {
return false;
}
PortalGunPortalEntity linked = portal.findLinkedPortal(serverLevel);
if (linked == null || !linked.isAlive()) {
return false;
}
AABB startBox = player.getBoundingBox().move(start.subtract(player.position()));
AABB endBox = startBox.move(end.subtract(start));
Vec3 crossingVelocity = PortalGunTransitMath.entityVelocityToWorld(player, velocity);
if (portal.resolveCrossing(player, startBox, endBox, crossingVelocity) == null) {
return false;
}
if (Boolean.getBoolean(DEBUG_TRAVERSAL_PROPERTY)) {
Antarchy.LOGGER.info("Portal gun traversal accepted path=client portal={} player={} end={} velocity={} linked={}",
portal.getUUID(), player.getUUID(), end, velocity, linked.getUUID());
}
portal.teleportEntity(player, linked, endBox, velocity, true);
return true;
}
public static boolean passProjectileThroughPortal(Projectile projectile, BlockHitResult hit) {
if (projectile instanceof PortalGunProjectileEntity || projectile.isRemoved() || projectile.isPassenger()) {
return false;
}
Vec3 location = hit.getLocation();
Vec3 velocity = projectile.getDeltaMovement();
for (PortalGunPortalEntity portal : findPortalsNearBounds(projectile.level(), new AABB(location, location).inflate(0.5D))) {
if (portal.getFacingDirection() != hit.getDirection() || portal.isTeleportCoolingDown(projectile)) {
continue;
}
PortalGunWorldPortalShape shape = portal.getWorldPortalShape();
PortalGunWorldPortalShape.PortalLocalCoords coords = shape.localCoords(location);
if (Math.abs(coords.depth()) > PROJECTILE_PLANE_TOLERANCE
|| Math.abs(coords.horizontal()) > shape.halfWidth()
|| Math.abs(coords.vertical()) > shape.halfHeight()
|| velocity.dot(shape.normal()) >= 0.0D) {
continue;
}
PortalGunPortalEntity linked = projectile.level() instanceof ServerLevel serverLevel ? portal.findLinkedPortal(serverLevel) : portal.getLinkedPortal();
if (linked == null || !linked.isAlive()) {
continue;
}
AABB crossingBox = projectile.getBoundingBox().move(location.subtract(projectile.position()));
if (projectile.level().isClientSide) {
PortalTransit transit = portal.computeTransit(projectile, linked, crossingBox, velocity, projectile.getLookAngle());
Vec3 exit = transit.position();
projectile.setPos(exit.x, exit.y, exit.z);
projectile.xo = exit.x;
projectile.yo = exit.y;
projectile.zo = exit.z;
projectile.setDeltaMovement(transit.velocity());
orientAlongVelocity(projectile, transit.velocity());
projectile.setPortalCooldown(TELEPORT_COOLDOWN_TICKS);
} else {
portal.teleportEntity(projectile, linked, crossingBox, velocity, false);
}
return true;
}
return false;
}
private static void orientAlongVelocity(Entity entity, Vec3 velocity) {
if (velocity.lengthSqr() < 1.0E-12D) {
return;
}
float yaw = (float) Math.toDegrees(Math.atan2(velocity.x, velocity.z));
float pitch = (float) Math.toDegrees(Math.atan2(velocity.y, velocity.horizontalDistance()));
entity.setYRot(yaw);
entity.setXRot(pitch);
entity.yRotO = yaw;
entity.xRotO = pitch;
}
private static boolean isFinite(Vec3 vector) {
return Double.isFinite(vector.x) && Double.isFinite(vector.y) && Double.isFinite(vector.z);
}
public static List<PortalGunPortalEntity> findPortalsNearBounds(Level level, AABB bounds) {
List<PortalGunPortalEntity> 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)) {
List<PortalGunPortalEntity> portals = null;
for (PortalGunPortalEntity portal : PortalGunPortalRegistry.all(level)) {
if (portal.isAlive() && portal.getWorldPortalShape().getBoundsForCulling().intersects(bounds)) {
if (portals == null) {
portals = new java.util.ArrayList<>(2);
}
portals.add(portal);
}
}
return portals;
return portals == null ? List.of() : portals;
}
@Override
public void setLevelCallback(net.minecraft.world.level.entity.EntityInLevelCallback callback) {
super.setLevelCallback(callback);
if (callback == net.minecraft.world.level.entity.EntityInLevelCallback.NULL) {
PortalGunPortalRegistry.untrack(this);
} else {
PortalGunPortalRegistry.track(this);
}
}
public boolean containsPoint(Vec3 position) {
@@ -490,6 +559,9 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity {
@Override
public void tick() {
super.tick();
if (!this.isRemoved()) {
PortalGunPortalRegistry.track(this);
}
int openingAnimationId = this.entityData.get(OPENING_ANIMATION_ID);
if (openingAnimationId != this.lastOpeningAnimationId) {
this.lastOpeningAnimationId = openingAnimationId;
@@ -505,16 +577,8 @@ 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)) {
if (this.gunId == null || !this.isStillRegistered(serverLevel)) {
this.discard();
return;
}
@@ -540,7 +604,7 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity {
}
private boolean isStillRegistered(ServerLevel level) {
return this.ownerId != null && PortalGunSavedData.isRegistered(level, this.ownerId, this.gunId, this.channelName, this.getPortalSide(), this.getUUID());
return PortalGunSavedData.isRegistered(level, this.gunId, this.getPortalSide(), this.getUUID());
}
private boolean hasValidSurface() {
@@ -576,36 +640,15 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity {
}
private void tickTeleport(PortalGunPortalEntity linked) {
List<Entity> entities = this.level().getEntities(this, this.getScanRange(), this::canTeleportEntity);
List<Entity> entities = this.level().getEntities(this, this.getScanRange(), entity -> !(entity instanceof Player) && this.canTeleportEntity(entity));
for (Entity entity : entities) {
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);
PortalCrossing crossing = this.resolveCrossing(entity, previousBox, currentBox, entity.getDeltaMovement());
if (crossing == null) {
if (entity instanceof Player && Boolean.getBoolean(DEBUG_TRAVERSAL_PROPERTY) && this.ageTicks % 20 == 0) {
Vec3 probe = this.teleportProbePosition(entity, currentBox);
double depth = this.getWorldPortalShape().localCoords(probe).depth();
if (depth < 1.5D) {
Antarchy.LOGGER.info("Portal gun traversal waiting portal={} player={} depth={} position={} linked={}",
this.getUUID(), entity.getUUID(), depth, entity.position(), linked.getUUID());
}
}
continue;
}
if (entity instanceof Player && Boolean.getBoolean(DEBUG_TRAVERSAL_PROPERTY)) {
Antarchy.LOGGER.info("Portal gun traversal accepted portal={} player={} probe={} offset={} linked={}",
this.getUUID(), entity.getUUID(), crossing.probe(), crossing.normalOffset(), linked.getUUID());
}
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 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);
this.teleportEntity(entity, linked, currentBox, entity.getDeltaMovement(), false);
}
}
@@ -620,56 +663,81 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity {
return this.teleportCooldowns.containsKey(entity.getUUID()) || entity.getPortalCooldown() > 0;
}
private void teleportEntity(Entity entity, PortalGunPortalEntity destination, Vec3 currentProbe, Vec3 entryMotion, double normalOffset) {
private void teleportEntity(Entity entity, PortalGunPortalEntity destination, AABB crossingBox, Vec3 velocity, boolean clientAuthoritative) {
List<EntityTeleportState> 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();
PortalTransit transit = this.computeTransit(entity, destination, crossingBox, velocity, entity.getLookAngle());
Vec3 exitPos = transit.position();
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();
state.entity().stopRiding();
}
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());
boolean root = member == entity;
Vec3 memberExitPos = root
? exitPos
: exitPos.add(PortalGunTransformUtil.transformVector(this, destination, state.position().subtract(rootOriginalPosition)));
Vec3 memberLook;
if (root) {
memberLook = transit.look();
} else if (AntarchyGravityApi.isGravityInverted(member)) {
Vec3 worldLook = PortalGunTransitMath.entityVelocityToWorld(member, state.look());
memberLook = PortalGunTransitMath.entityVelocityFromWorld(member,
PortalGunTransformUtil.transformVector(this, destination, worldLook).normalize());
} else {
memberLook = PortalGunTransformUtil.transformVector(this, destination, state.look()).normalize();
}
Vec3 memberMotion;
if (root) {
memberMotion = transit.velocity();
} else if (AntarchyGravityApi.isGravityInverted(member)) {
Vec3 worldMotion = PortalGunTransitMath.entityVelocityToWorld(member, state.motion());
memberMotion = PortalGunTransitMath.entityVelocityFromWorld(member,
PortalGunTransformUtil.transformVector(this, destination, worldMotion));
} else {
memberMotion = PortalGunTransformUtil.transformVector(this, destination, state.motion());
}
float yaw = PortalGunTransformUtil.yawFromLook(memberLook);
float pitch = PortalGunTransformUtil.pitchFromLook(memberLook);
if (member instanceof ServerPlayer player && destination.level() instanceof ServerLevel destinationLevel) {
float fallDistance = PortalGunTransitMath.exitFallDistance(member.fallDistance,
AntarchyGravityApi.isGravityInverted(member) ? PortalGunTransitMath.entityVelocityToWorld(member, memberMotion) : memberMotion,
member);
if (member instanceof ServerPlayer player && root && clientAuthoritative) {
player.moveTo(memberExitPos.x, memberExitPos.y, memberExitPos.z, yaw, pitch);
player.setYHeadRot(yaw);
player.setDeltaMovement(memberMotion);
player.connection.resetPosition();
player.serverLevel().getChunkSource().move(player);
} else if (member instanceof ServerPlayer player && destination.level() instanceof ServerLevel destinationLevel) {
player.teleportTo(destinationLevel, memberExitPos.x, memberExitPos.y, memberExitPos.z, yaw, pitch);
player.setDeltaMovement(memberMotion);
player.hasImpulse = true;
player.connection.send(new ClientboundSetEntityMotionPacket(player));
} 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.setDeltaMovement(memberMotion);
member.hasImpulse = true;
member.hurtMarked = true;
}
member.xo = member.getX();
member.yo = member.getY();
member.zo = member.getZ();
member.yRotO = yaw;
member.xRotO = pitch;
if (member instanceof Projectile) {
orientAlongVelocity(member, memberMotion);
}
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);
member.fallDistance = fallDistance;
this.applyTeleportCooldown(member, destination);
this.handleSpecialEntityPostTeleport(member);
}
@@ -684,12 +752,22 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity {
if (!(member instanceof ServerPlayer)) {
destinationLevel.getChunkSource().broadcastAndSend(member, new ClientboundTeleportEntityPacket(member));
destinationLevel.getChunkSource().broadcastAndSend(member, new ClientboundSetEntityMotionPacket(member));
syncTrackedPosition(destinationLevel, 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 static void syncTrackedPosition(ServerLevel level, Entity entity) {
Object tracked = ((ChunkMapAccessor) level.getChunkSource().chunkMap).antarchy$getEntityMap().get(entity.getId());
if (tracked instanceof TrackedEntityAccessor trackedEntity) {
ServerEntityAccessor serverEntity = (ServerEntityAccessor) trackedEntity.antarchy$getServerEntity();
serverEntity.antarchy$getPositionCodec().setBase(entity.trackingPosition());
serverEntity.antarchy$setLastSentMovement(entity.getDeltaMovement());
}
}
private void collectTeleportGroup(Entity entity, Entity vehicle, List<EntityTeleportState> group) {
group.add(new EntityTeleportState(entity, vehicle, entity.position(), entity.getLookAngle(), entity.getDeltaMovement()));
for (Entity passenger : entity.getPassengers()) {
@@ -697,37 +775,6 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity {
}
}
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() {
Iterator<Map.Entry<UUID, Integer>> iterator = this.teleportCooldowns.entrySet().iterator();
while (iterator.hasNext()) {
@@ -783,15 +830,9 @@ 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);
}
@@ -800,9 +841,7 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity {
tag.putInt("Side", this.getPortalSide().ordinal());
tag.putInt("Facing", this.getFacingDirection().get3DDataValue());
tag.putInt("UpAxis", this.getUpAxis().get3DDataValue());
tag.putInt("ChannelRed", this.getChannelRed());
tag.putInt("ChannelGreen", this.getChannelGreen());
tag.putInt("ChannelBlue", this.getChannelBlue());
tag.putInt("Variant", this.getVariant().id());
tag.putInt("PortalWidth", this.getPortalWidth());
tag.putInt("PortalHeight", this.getPortalHeight());
tag.putInt("SupportX", this.supportOrigin.getX());
@@ -839,12 +878,9 @@ 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));
@@ -854,16 +890,7 @@ 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 (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.entityData.set(VARIANT, PortalGunVariant.byId(tag.getInt("Variant")).id());
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"));
@@ -892,7 +919,7 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity {
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());
PortalGunSavedData.clearPortal(serverLevel.getServer(), this.gunId, this.getPortalSide(), this.getUUID(), serverLevel.dimension().location());
}
super.remove(reason);
if (destroyPortal && !level.isClientSide && ownerId != null && level instanceof ServerLevel serverLevel) {
@@ -971,40 +998,10 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity {
.orElseThrow(() -> new IllegalStateException("Missing portal gun sound event: " + soundId));
}
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};
}
}
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};
public record PortalCrossing(Vec3 probe) {
}
public record PortalCrossing(Vec3 probe, double normalOffset) {
}
public record PortalTransitTransform(Vec3 requestedPosition, Vec3 exitPosition, Vec3 movement) {
public record PortalTransit(AABB exitBox, Vec3 position, Vec3 velocity, Vec3 look) {
}
private record EntityTeleportState(Entity entity, Entity vehicle, Vec3 position, Vec3 look, Vec3 motion) {
@@ -0,0 +1,81 @@
package com.craisinlord.antarchy.content.portalgun;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentHashMap;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.AABB;
public final class PortalGunPortalRegistry {
private static final Map<Level, Map<UUID, PortalGunPortalEntity>> LEVELS = Collections.synchronizedMap(new WeakHashMap<>());
private PortalGunPortalRegistry() {
}
static void track(PortalGunPortalEntity portal) {
Level level = portal.level();
if (level == null) {
return;
}
Map<UUID, PortalGunPortalEntity> portals;
synchronized (LEVELS) {
portals = LEVELS.computeIfAbsent(level, ignored -> new ConcurrentHashMap<>());
}
portals.put(portal.getUUID(), portal);
}
static void untrack(PortalGunPortalEntity portal) {
Map<UUID, PortalGunPortalEntity> portals = portalsIn(portal.level());
if (portals != null) {
portals.remove(portal.getUUID(), portal);
}
}
public static Collection<PortalGunPortalEntity> all(Level level) {
Map<UUID, PortalGunPortalEntity> portals = portalsIn(level);
return portals == null ? List.of() : portals.values();
}
public static PortalGunPortalEntity find(Level level, UUID portalId) {
if (portalId == null) {
return null;
}
Map<UUID, PortalGunPortalEntity> portals = portalsIn(level);
if (portals == null) {
return null;
}
PortalGunPortalEntity portal = portals.get(portalId);
return portal != null && portal.isAlive() ? portal : null;
}
public static List<PortalGunPortalEntity> near(Level level, AABB bounds) {
Map<UUID, PortalGunPortalEntity> portals = portalsIn(level);
if (portals == null || portals.isEmpty()) {
return List.of();
}
List<PortalGunPortalEntity> nearby = null;
for (PortalGunPortalEntity portal : portals.values()) {
if (portal.isAlive() && portal.getPortalInsides().intersects(bounds)) {
if (nearby == null) {
nearby = new ArrayList<>(2);
}
nearby.add(portal);
}
}
return nearby == null ? List.of() : nearby;
}
private static Map<UUID, PortalGunPortalEntity> portalsIn(Level level) {
if (level == null) {
return null;
}
synchronized (LEVELS) {
return LEVELS.get(level);
}
}
}
@@ -1,6 +1,9 @@
package com.craisinlord.antarchy.content.portalgun;
import com.craisinlord.antarchy.content.item.PortalGunItem;
import com.craisinlord.antarchy.content.gravity.AntarchyGravityApi;
import com.craisinlord.antarchy.content.gravity.AntarchyGravityDirection;
import com.craisinlord.antarchy.content.gravity.AntarchyGravityTransition;
import com.craisinlord.antarchy.config.AntarchySettings;
import java.util.Comparator;
import java.util.LinkedHashMap;
@@ -61,6 +64,10 @@ public class PortalGunProjectileEntity extends ThrowableItemProjectile {
private static final EntityDataAccessor<Float> PROJECTILE_SPEED = SynchedEntityData.defineId(PortalGunProjectileEntity.class, EntityDataSerializers.FLOAT);
private static final EntityDataAccessor<Boolean> PASS_THROUGH_GLASS = SynchedEntityData.defineId(PortalGunProjectileEntity.class, EntityDataSerializers.BOOLEAN);
private static final EntityDataAccessor<Boolean> PASS_THROUGH_LIQUID = SynchedEntityData.defineId(PortalGunProjectileEntity.class, EntityDataSerializers.BOOLEAN);
private static final int TRAIL_PARTICLES_PER_TICK = 4;
private static final float TRAIL_DUST_SCALE = 0.45F;
private static final float IMPACT_DUST_SCALE = 0.8F;
private static final int IMPACT_PARTICLES = 14;
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;
@@ -109,6 +116,7 @@ public class PortalGunProjectileEntity extends ThrowableItemProjectile {
}
public void configure(PortalGunPortalEntity.PortalSide side, UUID gunId, ItemStack gunStack) {
AntarchyGravityApi.setAirborneGravityDirection(this, AntarchyGravityDirection.DOWN, false, AntarchyGravityTransition.INSTANT);
this.entityData.set(SIDE, side.ordinal());
this.gunId = gunId;
this.setItem(gunStack.copyWithCount(1));
@@ -134,6 +142,10 @@ public class PortalGunProjectileEntity extends ThrowableItemProjectile {
return this.entityData.get(PROJECTILE_SPEED);
}
public PortalGunVariant getVariant() {
return this.getItem().getItem() instanceof PortalGunItem portalGun ? portalGun.getVariant() : PortalGunVariant.DEFAULT;
}
public PortalGunPortalEntity.PortalSide getPortalSide() {
return this.entityData.get(SIDE) == PortalGunPortalEntity.PortalSide.ORANGE.ordinal() ? PortalGunPortalEntity.PortalSide.ORANGE : PortalGunPortalEntity.PortalSide.BLUE;
}
@@ -265,14 +277,12 @@ public class PortalGunProjectileEntity extends ThrowableItemProjectile {
);
}
}
if (this.level().isClientSide) {
if (this.level().isClientSide && this.tickCount > 1) {
net.minecraft.core.particles.DustParticleOptions dust = this.trailDust(TRAIL_DUST_SCALE);
Vec3 point = this.position();
if (this.getPortalSide() == PortalGunPortalEntity.PortalSide.BLUE) {
this.level().addParticle(net.minecraft.core.particles.ParticleTypes.SOUL_FIRE_FLAME, point.x, point.y, point.z, 0.0D, 0.0D, 0.0D);
this.level().addParticle(net.minecraft.core.particles.ParticleTypes.END_ROD, point.x, point.y, point.z, 0.0D, 0.0D, 0.0D);
} else {
this.level().addParticle(net.minecraft.core.particles.ParticleTypes.FLAME, point.x, point.y, point.z, 0.0D, 0.0D, 0.0D);
this.level().addParticle(net.minecraft.core.particles.ParticleTypes.CRIT, point.x, point.y, point.z, 0.0D, 0.0D, 0.0D);
for (int i = 0; i < TRAIL_PARTICLES_PER_TICK; i++) {
Vec3 trailPoint = point.subtract(motion.scale((double) i / TRAIL_PARTICLES_PER_TICK));
this.level().addParticle(dust, trailPoint.x, trailPoint.y, trailPoint.z, 0.0D, 0.0D, 0.0D);
}
}
}
@@ -368,13 +378,13 @@ public class PortalGunProjectileEntity extends ThrowableItemProjectile {
}
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);
level.sendParticles(net.minecraft.core.particles.ParticleTypes.END_ROD, impactPos.x, impactPos.y, impactPos.z, 6, 0.04D, 0.04D, 0.04D, 0.01D);
return;
}
level.sendParticles(net.minecraft.core.particles.ParticleTypes.FLAME, impactPos.x, impactPos.y, impactPos.z, 10, 0.08D, 0.08D, 0.08D, 0.01D);
level.sendParticles(net.minecraft.core.particles.ParticleTypes.CRIT, impactPos.x, impactPos.y, impactPos.z, 6, 0.04D, 0.04D, 0.04D, 0.01D);
level.sendParticles(this.trailDust(IMPACT_DUST_SCALE), impactPos.x, impactPos.y, impactPos.z, IMPACT_PARTICLES, 0.12D, 0.12D, 0.12D, 0.02D);
}
private net.minecraft.core.particles.DustParticleOptions trailDust(float scale) {
int color = this.getVariant().color(this.getPortalSide());
return new net.minecraft.core.particles.DustParticleOptions(
new org.joml.Vector3f((color >> 16 & 0xFF) / 255.0F, (color >> 8 & 0xFF) / 255.0F, (color & 0xFF) / 255.0F), scale);
}
private void syncSpawnPosition(Vec3 pos) {
@@ -2,9 +2,7 @@ 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;
@@ -18,17 +16,13 @@ import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.saveddata.SavedData;
public final class PortalGunSavedData extends SavedData {
private static final String ID = "antarchy_portal_gun_pairs";
private static final String ID = "antarchy_portal_gun_pairs_v2";
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 static final String CHANNELS_KEY = "Channels";
private final Map<PairKey, PairRecord> pairs = new HashMap<>();
private final Map<UUID, PairRecord> legacyPairs = new HashMap<>();
private final Map<OwnerChannelKey, UUID> channelGunIds = new HashMap<>();
public static PortalGunSavedData create() {
return new PortalGunSavedData();
@@ -38,23 +32,13 @@ public final class PortalGunSavedData extends SavedData {
PortalGunSavedData data = new PortalGunSavedData();
for (net.minecraft.nbt.Tag rawEntry : tag.getList(ENTRIES_KEY, net.minecraft.nbt.Tag.TAG_COMPOUND)) {
CompoundTag entryTag = (CompoundTag) rawEntry;
UUID owner = entryTag.getUUID(OWNER_KEY);
ResourceLocation dimension = ResourceLocation.tryParse(entryTag.getString(DIMENSION_KEY));
if (dimension == null || !entryTag.hasUUID(GUN_KEY)) {
continue;
}
UUID blue = entryTag.hasUUID(BLUE_KEY) ? entryTag.getUUID(BLUE_KEY) : null;
UUID orange = entryTag.hasUUID(ORANGE_KEY) ? entryTag.getUUID(ORANGE_KEY) : null;
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));
}
data.pairs.put(new PairKey(dimension, entryTag.getUUID(GUN_KEY)), new PairRecord(blue, orange));
}
return data;
}
@@ -64,42 +48,20 @@ public final class PortalGunSavedData extends SavedData {
ListTag entries = new ListTag();
for (Map.Entry<PairKey, PairRecord> entry : this.pairs.entrySet()) {
CompoundTag entryTag = new CompoundTag();
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());
entryTag.putUUID(GUN_KEY, entry.getKey().gunId());
if (entry.getValue().bluePortalId() != null) {
entryTag.putUUID(BLUE_KEY, entry.getValue().bluePortalId());
}
if (entry.getValue().orangePortalId() != null) {
entryTag.putUUID(ORANGE_KEY, entry.getValue().orangePortalId());
}
writePair(entryTag, entry.getValue());
entries.add(entryTag);
}
for (Map.Entry<UUID, PairRecord> 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<OwnerChannelKey, UUID> 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),
@@ -107,132 +69,57 @@ public final class PortalGunSavedData extends SavedData {
);
}
public static Optional<UUID> 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));
public static Optional<UUID> getPortalId(MinecraftServer server, UUID gunId, PortalGunPortalEntity.PortalSide side, ResourceLocation dimension) {
if (gunId == null) {
return Optional.empty();
}
PairRecord record = get(server).pairs.get(new PairKey(dimension, gunId));
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 Optional<UUID> getPortalId(MinecraftServer server, UUID owner, UUID gunId, PortalGunPortalEntity.PortalSide side, ResourceLocation dimension) {
return getPortalId(server, owner, gunId, String.valueOf(gunId), side, dimension);
public static boolean isRegistered(ServerLevel level, UUID gunId, PortalGunPortalEntity.PortalSide side, UUID portalId) {
return portalId != null && getPortalId(level.getServer(), gunId, side, level.dimension().location()).map(portalId::equals).orElse(false);
}
public static Optional<UUID> getGunIdForPortal(MinecraftServer server, UUID owner, ResourceLocation dimension, UUID portalId) {
PortalGunSavedData data = get(server);
data.migrateLegacyPair(server, owner);
for (Map.Entry<PairKey, PairRecord> 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());
}
public static void setPortal(ServerLevel level, UUID gunId, PortalGunPortalEntity.PortalSide side, UUID portalId) {
if (gunId == null) {
return;
}
return Optional.empty();
}
public static Optional<String> getChannelNameForPortal(MinecraftServer server, UUID owner, ResourceLocation dimension, UUID portalId) {
PortalGunSavedData data = get(server);
for (Map.Entry<PairKey, PairRecord> 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);
PairKey key = new PairKey(level.dimension().location(), gunId);
PairRecord current = data.pairs.getOrDefault(key, new PairRecord(null, null));
PairRecord updated = side == PortalGunPortalEntity.PortalSide.BLUE
data.put(key, side == PortalGunPortalEntity.PortalSide.BLUE
? new PairRecord(portalId, current.orangePortalId())
: new PairRecord(current.bluePortalId(), portalId);
if (updated.bluePortalId() == null && updated.orangePortalId() == null) {
data.pairs.remove(key);
} else {
data.pairs.put(key, updated);
: new PairRecord(current.bluePortalId(), portalId));
}
public static void clearPortal(MinecraftServer server, UUID gunId, PortalGunPortalEntity.PortalSide side, UUID portalId, ResourceLocation dimension) {
if (gunId == null) {
return;
}
data.setDirty();
}
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);
UUID resolvedGunId = data.resolveChannelGunId(owner, channelName, gunId);
data.migrateLegacyPair(server, owner);
data.adoptLegacyPairs(server, owner, resolvedGunId, channelName);
PairKey key = new PairKey(owner, dimension, resolvedGunId);
PairKey key = new PairKey(dimension, gunId);
PairRecord current = data.pairs.get(key);
if (current == null) {
return;
}
UUID blue = current.bluePortalId();
UUID orange = current.orangePortalId();
if (side == PortalGunPortalEntity.PortalSide.BLUE && portalId.equals(blue)) {
blue = null;
}
if (side == PortalGunPortalEntity.PortalSide.ORANGE && portalId.equals(orange)) {
orange = null;
}
if (blue == null && orange == null) {
data.pairs.remove(key);
} else {
data.pairs.put(key, new PairRecord(blue, orange));
}
data.setDirty();
UUID blue = side == PortalGunPortalEntity.PortalSide.BLUE && portalId.equals(current.bluePortalId()) ? null : current.bluePortalId();
UUID orange = side == PortalGunPortalEntity.PortalSide.ORANGE && portalId.equals(current.orangePortalId()) ? null : current.orangePortalId();
data.put(key, new PairRecord(blue, orange));
}
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) {
public static void clearAllPortals(ServerLevel level, UUID gunId) {
if (gunId == null) {
return;
}
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<UUID> 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)) {
if (!entry.getKey().gunId().equals(gunId)) {
return false;
}
addPortalIds(portalIds, entry.getValue());
@@ -244,20 +131,15 @@ public final class PortalGunSavedData extends SavedData {
}
}
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) {
public static void clearPortalSide(ServerLevel level, UUID gunId, PortalGunPortalEntity.PortalSide side) {
if (gunId == null) {
return;
}
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<UUID> portalIds = new HashSet<>();
for (Map.Entry<PairKey, PairRecord> 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)) {
if (!entry.getKey().gunId().equals(gunId)) {
continue;
}
PairRecord pair = entry.getValue();
@@ -266,23 +148,17 @@ public final class PortalGunSavedData extends SavedData {
continue;
}
portalIds.add(removed);
PairRecord remaining = side == PortalGunPortalEntity.PortalSide.BLUE
data.put(entry.getKey(), 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);
}
: new PairRecord(pair.bluePortalId(), null));
}
data.setDirty();
for (UUID portalId : portalIds) {
discardIfLoaded(server, portalId);
}
}
public static PortalGunPortalEntity findLoadedPortal(ServerLevel level, UUID owner, UUID gunId, PortalGunPortalEntity.PortalSide side) {
Optional<UUID> portalId = getPortalId(level.getServer(), owner, gunId, side, level.dimension().location());
public static PortalGunPortalEntity findLoadedPortal(ServerLevel level, UUID gunId, PortalGunPortalEntity.PortalSide side) {
Optional<UUID> portalId = getPortalId(level.getServer(), gunId, side, level.dimension().location());
if (portalId.isEmpty()) {
return null;
}
@@ -290,143 +166,13 @@ public final class PortalGunSavedData extends SavedData {
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<UUID> 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);
private void put(PairKey key, PairRecord record) {
if (record.bluePortalId() == null && record.orangePortalId() == null) {
this.pairs.remove(key);
} else {
this.pairs.put(key, record);
}
this.setDirty();
return channelGunId;
}
private void migrateGunPairs(UUID owner, UUID previousGunId, UUID channelGunId) {
for (Map.Entry<PairKey, PairRecord> 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<PairKey, PairRecord> 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()) {
if (level.getEntity(portalId) instanceof PortalGunPortalEntity portal && !portal.isRemoved()) {
return level;
}
}
return null;
}
private static void addPortalIds(Set<UUID> portalIds, PairRecord pair) {
@@ -439,16 +185,15 @@ public final class PortalGunSavedData extends SavedData {
}
private static void discardIfLoaded(MinecraftServer server, UUID portalId) {
ServerLevel level = findPortalLevel(server, portalId);
if (level != null && level.getEntity(portalId) instanceof PortalGunPortalEntity portal) {
portal.discard();
for (ServerLevel level : server.getAllLevels()) {
if (level.getEntity(portalId) instanceof PortalGunPortalEntity portal && !portal.isRemoved()) {
portal.discard();
return;
}
}
}
private record PairKey(UUID owner, ResourceLocation dimension, UUID gunId) {
}
private record OwnerChannelKey(UUID owner, String channelName) {
private record PairKey(ResourceLocation dimension, UUID gunId) {
}
private record PairRecord(UUID bluePortalId, UUID orangePortalId) {
@@ -0,0 +1,18 @@
package com.craisinlord.antarchy.content.portalgun;
import com.craisinlord.antarchy.content.network.PortalGunTransitPayload;
import net.minecraft.server.level.ServerPlayer;
public final class PortalGunTransitHandler {
private PortalGunTransitHandler() {
}
public static void handle(ServerPlayer player, PortalGunTransitPayload payload) {
if (player == null) {
return;
}
if (!PortalGunPortalEntity.handleClientTransit(player, payload.portalId(), payload.start(), payload.end(), payload.velocity())) {
player.connection.teleport(player.getX(), player.getY(), player.getZ(), player.getYRot(), player.getXRot());
}
}
}
@@ -0,0 +1,138 @@
package com.craisinlord.antarchy.content.portalgun;
import com.craisinlord.antarchy.content.gravity.AntarchyGravityApi;
import com.craisinlord.antarchy.content.gravity.AntarchyGravityDirection;
import com.craisinlord.antarchy.content.gravity.AntarchyGravityRotationUtil;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
public final class PortalGunTransitMath {
public static final double EXIT_CLEARANCE = 0.02D;
public static final double LEADING_FACE_TOLERANCE = 0.05D;
private static final double UPWARD_EXIT_NORMAL_Y = 0.5D;
private PortalGunTransitMath() {
}
public static double extentAlong(AABB box, Vec3 axis) {
return (box.maxX - box.minX) * 0.5D * Math.abs(axis.x)
+ (box.maxY - box.minY) * 0.5D * Math.abs(axis.y)
+ (box.maxZ - box.minZ) * 0.5D * Math.abs(axis.z);
}
public static boolean fitsOpening(PortalGunWorldPortalShape shape, AABB box, double tolerance) {
PortalGunWorldPortalShape.PortalLocalCoords center = shape.localCoords(box.getCenter());
return Math.abs(center.horizontal()) + extentAlong(box, shape.right()) <= shape.halfWidth() + tolerance
&& Math.abs(center.vertical()) + extentAlong(box, shape.up()) <= shape.halfHeight() + tolerance;
}
public static boolean centredOverOpening(PortalGunWorldPortalShape shape, AABB box) {
PortalGunWorldPortalShape.PortalLocalCoords center = shape.localCoords(box.getCenter());
return Math.abs(center.horizontal()) <= shape.halfWidth() && Math.abs(center.vertical()) <= shape.halfHeight();
}
public static boolean crossesWithBody(PortalGunWorldPortalShape shape, AABB previousBox, AABB currentBox, Vec3 attemptedMovement) {
double previousDepth = shape.localCoords(previousBox.getCenter()).depth();
double currentDepth = shape.localCoords(currentBox.getCenter()).depth();
if (previousDepth <= 0.0D) {
return false;
}
boolean movingInward = attemptedMovement.dot(shape.normal()) < -1.0E-6D || currentDepth < previousDepth - 1.0E-6D;
if (!movingInward) {
return false;
}
if (currentDepth - extentAlong(currentBox, shape.normal()) > LEADING_FACE_TOLERANCE) {
return false;
}
return centredOverOpening(shape, currentBox);
}
public static Vec3 transformVector(PortalGunWorldPortalShape source, PortalGunWorldPortalShape destination, Vec3 vector) {
return PortalGunTransformUtil.transformVector(source, destination, vector);
}
public static AABB exitBox(PortalGunWorldPortalShape source, PortalGunWorldPortalShape destination, AABB box) {
PortalGunWorldPortalShape.PortalLocalCoords local = source.localCoords(box.getCenter());
double horizontal = clampLateral(-local.horizontal(), destination.halfWidth(), extentAlong(box, destination.right()));
double vertical = clampLateral(local.vertical(), destination.halfHeight(), extentAlong(box, destination.up()));
double depth = extentAlong(box, destination.normal()) + EXIT_CLEARANCE;
Vec3 center = destination.center()
.add(destination.right().scale(horizontal))
.add(destination.up().scale(vertical))
.add(destination.normal().scale(depth));
Vec3 offset = center.subtract(box.getCenter());
return box.move(offset);
}
public static Vec3 feetPosition(AABB box) {
return new Vec3((box.minX + box.maxX) * 0.5D, box.minY, (box.minZ + box.maxZ) * 0.5D);
}
public static Vec3 entityPosition(AABB box, Entity entity) {
return entityPosition(box, AntarchyGravityApi.isGravityInverted(entity));
}
public static Vec3 entityPosition(AABB box, boolean inverted) {
if (!inverted) {
return feetPosition(box);
}
return new Vec3((box.minX + box.maxX) * 0.5D, box.maxY, (box.minZ + box.maxZ) * 0.5D);
}
public static Vec3 entityVelocityToWorld(Entity entity, Vec3 velocity) {
return entityVelocityToWorld(velocity, AntarchyGravityApi.isGravityInverted(entity));
}
public static Vec3 entityVelocityToWorld(Vec3 velocity, boolean inverted) {
if (!inverted) {
return velocity;
}
return AntarchyGravityRotationUtil.vecPlayerToWorld(velocity, AntarchyGravityDirection.UP);
}
public static Vec3 entityVelocityFromWorld(Entity entity, Vec3 velocity) {
return entityVelocityFromWorld(velocity, AntarchyGravityApi.isGravityInverted(entity));
}
public static Vec3 entityVelocityFromWorld(Vec3 velocity, boolean inverted) {
if (!inverted) {
return velocity;
}
return AntarchyGravityRotationUtil.vecWorldToPlayer(velocity, AntarchyGravityDirection.UP);
}
public static Vec3 exitVelocity(PortalGunWorldPortalShape source, PortalGunWorldPortalShape destination, Vec3 velocity, double minUpwardExitSpeed, double maxExitSpeed) {
Vec3 exit = transformVector(source, destination, velocity);
Vec3 normal = destination.normal();
if (minUpwardExitSpeed > 0.0D && normal.y > UPWARD_EXIT_NORMAL_Y) {
double outward = exit.dot(normal);
if (outward < minUpwardExitSpeed) {
exit = exit.add(normal.scale(minUpwardExitSpeed - outward));
}
}
if (maxExitSpeed > 0.0D && exit.lengthSqr() > maxExitSpeed * maxExitSpeed) {
exit = exit.normalize().scale(maxExitSpeed);
}
return exit;
}
public static float exitFallDistance(float fallDistance, Vec3 exitVelocity) {
return exitVelocity.y < 0.0D ? fallDistance : 0.0F;
}
public static float exitFallDistance(float fallDistance, Vec3 exitVelocity, Entity entity) {
if (!AntarchyGravityApi.isGravityInverted(entity)) {
return exitFallDistance(fallDistance, exitVelocity);
}
return exitVelocity.y > 0.0D ? fallDistance : 0.0F;
}
private static double clampLateral(double coordinate, double halfSize, double extent) {
double limit = halfSize - extent;
if (limit <= 0.0D) {
return 0.0D;
}
return Math.max(-limit, Math.min(limit, coordinate));
}
}
@@ -0,0 +1,66 @@
package com.craisinlord.antarchy.content.portalgun;
import com.craisinlord.antarchy.Antarchy;
import net.minecraft.resources.ResourceLocation;
public enum PortalGunVariant {
DEFAULT("", 0x40A0FF, 0xFF961E, "blue", "orange"),
ATLAS("atlas", 0x53A6D0, 0x6A2BE0, "blue", "purple"),
P_BODY("p_body", 0xF9D660, 0xC0282A, "yellow", "red");
private final String key;
private final int blueColor;
private final int orangeColor;
private final String blueColorName;
private final String orangeColorName;
private final ResourceLocation crosshairTexture;
private final ResourceLocation modelTexture;
private final ResourceLocation blueVortexTexture;
private final ResourceLocation orangeVortexTexture;
PortalGunVariant(String key, int blueColor, int orangeColor, String blueColorName, String orangeColorName) {
this.key = key;
this.blueColor = blueColor;
this.orangeColor = orangeColor;
this.blueColorName = blueColorName;
this.orangeColorName = orangeColorName;
String prefix = key.isEmpty() ? "" : key + "_";
this.crosshairTexture = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "textures/gui/portal_gun_" + prefix + "crosshair.png");
this.modelTexture = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "textures/models/item/portal_gun" + (key.isEmpty() ? "" : "_" + key) + ".png");
this.blueVortexTexture = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "textures/entity/portal_gun/portal_" + prefix + "blue_vortex.png");
this.orangeVortexTexture = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "textures/entity/portal_gun/portal_" + prefix + "orange_vortex.png");
}
public String key() {
return this.key;
}
public int color(PortalGunPortalEntity.PortalSide side) {
return side == PortalGunPortalEntity.PortalSide.BLUE ? this.blueColor : this.orangeColor;
}
public String colorTranslationKey(PortalGunPortalEntity.PortalSide side) {
return "tooltip.antarchy.portal_gun.color." + (side == PortalGunPortalEntity.PortalSide.BLUE ? this.blueColorName : this.orangeColorName);
}
public ResourceLocation crosshairTexture() {
return this.crosshairTexture;
}
public ResourceLocation modelTexture() {
return this.modelTexture;
}
public ResourceLocation vortexTexture(PortalGunPortalEntity.PortalSide side) {
return side == PortalGunPortalEntity.PortalSide.BLUE ? this.blueVortexTexture : this.orangeVortexTexture;
}
public int id() {
return this.ordinal();
}
public static PortalGunVariant byId(int id) {
PortalGunVariant[] values = values();
return id >= 0 && id < values.length ? values[id] : DEFAULT;
}
}
@@ -52,6 +52,19 @@ public record PortalGunWorldPortalShape(
};
}
public Vec3[] getOutline(double normalOffset, int segments, double radiusHorizontal, double radiusVertical) {
int count = Math.max(3, segments);
Vec3 center = this.center.add(this.normal.scale(normalOffset));
Vec3[] outline = new Vec3[count];
for (int index = 0; index < count; index++) {
double angle = Math.PI * 2.0D * index / count;
outline[index] = center
.add(this.right.scale(Math.cos(angle) * radiusHorizontal))
.add(this.up.scale(Math.sin(angle) * radiusVertical));
}
return outline;
}
public PortalLocalCoords localCoords(Vec3 position) {
Vec3 relative = position.subtract(this.center);
return new PortalLocalCoords(relative.dot(this.right), relative.dot(this.up), relative.dot(this.normal));
@@ -28,10 +28,12 @@ import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
/** Places the authored King's Tree as independently generated chunk slices on a fixed world grid. */
public final class KingsTreeGridFeature extends Feature<NoneFeatureConfiguration> {
private static final Map<ServerLevel, Set<Long>> CLAIMED_TREE_KINGS = new WeakHashMap<>();
private static final ConcurrentLinkedQueue<PendingKingSpawn> PENDING_KING_SPAWNS = new ConcurrentLinkedQueue<>();
private static final int TREE_MIN_X = -191;
private static final int TREE_MAX_X = 192;
private static final int TREE_MIN_Z = -227;
@@ -108,53 +110,70 @@ public final class KingsTreeGridFeature extends Feature<NoneFeatureConfiguration
}
}
if (centralTilePlaced) {
spawnTreeKing(level, centerX, centerZ, baseY);
queueTreeKing(level.getLevel(), centerX, centerZ, baseY);
}
return placed;
}
private static void spawnTreeKing(WorldGenLevel level, int centerX, int centerZ, int baseY) {
var serverLevel = level.getLevel();
private static void queueTreeKing(ServerLevel serverLevel, int centerX, int centerZ, int baseY) {
long treeKey = ((long) centerX << 32) ^ (centerZ & 0xffffffffL);
synchronized (serverLevel) {
synchronized (CLAIMED_TREE_KINGS) {
if (!CLAIMED_TREE_KINGS.computeIfAbsent(serverLevel, ignored -> new java.util.HashSet<>()).add(treeKey)) {
return;
}
}
Vec3 treeCenter = new Vec3(centerX + 0.5D, 0.0D, centerZ + 0.5D);
AABB searchBox = new AABB(
centerX - 512.0D, level.getMinBuildHeight(), centerZ - 512.0D,
centerX + 512.0D, level.getMaxBuildHeight(), centerZ + 512.0D);
List<KingEntity> treeKings = serverLevel.getEntitiesOfClass(KingEntity.class, searchBox,
king -> king.isAlive() && king.isTreePatrolFor(treeCenter));
if (!treeKings.isEmpty()) {
// Repair older worlds that already contain duplicate tree-bound Kings.
for (int index = 1; index < treeKings.size(); index++) {
treeKings.get(index).discard();
}
synchronized (CLAIMED_TREE_KINGS) {
if (!CLAIMED_TREE_KINGS.computeIfAbsent(serverLevel, ignored -> new java.util.HashSet<>()).add(treeKey)) {
return;
}
KingEntity king = AntarchyObjects.KING.get().create(serverLevel);
if (king == null) {
synchronized (CLAIMED_TREE_KINGS) {
CLAIMED_TREE_KINGS.getOrDefault(serverLevel, Set.of()).remove(treeKey);
}
return;
}
double minimumY = baseY + 24.0D;
double maximumY = baseY + 304.0D;
// Introduce the King high in the canopy, where his tree patrol is most visible.
double spawnY = minimumY + (maximumY - minimumY) * 0.65D;
Vec3 spawn = findSafeKingSpawn(serverLevel, king, centerX, centerZ, spawnY);
king.moveTo(spawn.x, spawn.y, spawn.z,
(float) (Math.atan2(centerZ + 0.5D - spawn.z, centerX + 0.5D - spawn.x)
* 180.0D / Math.PI) - 90.0F,
0.0F);
king.setTreePatrolHome(treeCenter, minimumY, maximumY, 0.0D);
king.setPersistenceRequired();
serverLevel.addFreshEntity(king);
}
PENDING_KING_SPAWNS.add(new PendingKingSpawn(serverLevel, centerX, centerZ, baseY));
}
public static void tickPendingSpawns(ServerLevel level) {
int pendingCount = PENDING_KING_SPAWNS.size();
for (int index = 0; index < pendingCount; index++) {
PendingKingSpawn pending = PENDING_KING_SPAWNS.poll();
if (pending == null) {
return;
}
if (pending.level() != level || !spawnTreeKing(pending)) {
PENDING_KING_SPAWNS.add(pending);
}
}
}
private static boolean spawnTreeKing(PendingKingSpawn pending) {
ServerLevel serverLevel = pending.level();
int centerX = pending.centerX();
int centerZ = pending.centerZ();
int baseY = pending.baseY();
Vec3 treeCenter = new Vec3(centerX + 0.5D, 0.0D, centerZ + 0.5D);
AABB searchBox = new AABB(
centerX - 512.0D, serverLevel.getMinBuildHeight(), centerZ - 512.0D,
centerX + 512.0D, serverLevel.getMaxBuildHeight(), centerZ + 512.0D);
List<KingEntity> treeKings = serverLevel.getEntitiesOfClass(KingEntity.class, searchBox,
king -> king.isAlive() && king.isTreePatrolFor(treeCenter));
if (!treeKings.isEmpty()) {
for (int index = 1; index < treeKings.size(); index++) {
treeKings.get(index).discard();
}
return true;
}
KingEntity king = AntarchyObjects.KING.get().create(serverLevel);
if (king == null) {
return true;
}
double minimumY = baseY + 24.0D;
double maximumY = baseY + 304.0D;
double spawnY = minimumY + (maximumY - minimumY) * 0.65D;
Vec3 spawn = findSafeKingSpawn(serverLevel, king, centerX, centerZ, spawnY);
if (spawn == null) {
return false;
}
king.moveTo(spawn.x, spawn.y, spawn.z,
(float) (Math.atan2(centerZ + 0.5D - spawn.z, centerX + 0.5D - spawn.x)
* 180.0D / Math.PI) - 90.0F,
0.0F);
king.setTreePatrolHome(treeCenter, minimumY, maximumY, 0.0D);
king.setPersistenceRequired();
serverLevel.addFreshEntity(king);
return true;
}
private static Vec3 findSafeKingSpawn(ServerLevel level, KingEntity king,
@@ -169,7 +188,7 @@ public final class KingsTreeGridFeature extends Feature<NoneFeatureConfiguration
for (double heightOffset : heightOffsets) {
Vec3 candidate = new Vec3(x, spawnY + heightOffset, z);
BlockPos blockPos = BlockPos.containing(candidate);
if (!level.hasChunksAt(blockPos.offset(-8, -8, -8), blockPos.offset(8, 16, 8))) {
if (!areCollisionChunksLoaded(level, blockPos.offset(-8, -8, -8), blockPos.offset(8, 16, 8))) {
continue;
}
king.moveTo(candidate.x, candidate.y, candidate.z, 0.0F, 0.0F);
@@ -179,7 +198,22 @@ public final class KingsTreeGridFeature extends Feature<NoneFeatureConfiguration
}
}
}
return new Vec3(centerX + 64.5D, spawnY + 32.0D, centerZ + 0.5D);
return null;
}
private static boolean areCollisionChunksLoaded(ServerLevel level, BlockPos min, BlockPos max) {
var chunkSource = level.getChunkSource();
for (int chunkX = min.getX() >> 4; chunkX <= max.getX() >> 4; chunkX++) {
for (int chunkZ = min.getZ() >> 4; chunkZ <= max.getZ() >> 4; chunkZ++) {
if (chunkSource.getChunkNow(chunkX, chunkZ) == null) {
return false;
}
}
}
return true;
}
private record PendingKingSpawn(ServerLevel level, int centerX, int centerZ, int baseY) {
}
private static ResourceLocation tileLocation(int tileX, int tileZ) {
@@ -3,6 +3,7 @@ package com.craisinlord.antarchy.mixins;
import com.craisinlord.antarchy.content.portalgun.PortalGunCollisionHelper;
import com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.MoverType;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
import org.spongepowered.asm.mixin.Mixin;
@@ -12,34 +13,30 @@ 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)
@Mixin(value = Entity.class, priority = 1100)
public abstract class PortalGunEntityCollisionMixin {
@Unique
private AABB antarchy$portalGunStartBox;
@Unique
private AABB antarchy$portalGunMoveStartBox;
@Unique
private Vec3 antarchy$portalGunMoveStartPosition;
private Vec3 antarchy$portalGunMoveStartVelocity;
@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();
private void antarchy$capturePortalGunMovementStart(MoverType type, Vec3 movement, CallbackInfo ci) {
this.antarchy$portalGunMoveStartBox = ((Entity) (Object) this).getBoundingBox();
this.antarchy$portalGunMoveStartVelocity = ((Entity) (Object) this).getDeltaMovement();
}
@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)
);
}
private void antarchy$teleportMobOnPortalCrossing(MoverType type, Vec3 movement, CallbackInfo ci) {
AABB startBox = this.antarchy$portalGunMoveStartBox;
Vec3 startVelocity = this.antarchy$portalGunMoveStartVelocity;
this.antarchy$portalGunMoveStartBox = null;
this.antarchy$portalGunMoveStartPosition = null;
this.antarchy$portalGunMoveStartVelocity = null;
if (startBox != null && startVelocity != null) {
PortalGunPortalEntity.teleportEntityAfterMovement((Entity) (Object) this, startBox, startVelocity);
}
}
@Inject(method = "collide", at = @At("HEAD"))
@@ -53,8 +50,11 @@ public abstract class PortalGunEntityCollisionMixin {
if (this.antarchy$portalGunStartBox == null) {
return;
}
Vec3 adjusted = PortalGunCollisionHelper.resolveCollision(entity, this.antarchy$portalGunStartBox, movement, cir.getReturnValue());
cir.setReturnValue(adjusted);
Vec3 collided = cir.getReturnValue();
Vec3 adjusted = PortalGunCollisionHelper.resolveCollision(entity, this.antarchy$portalGunStartBox, movement, collided);
if (adjusted != collided) {
cir.setReturnValue(adjusted);
}
}
}
@@ -15,7 +15,7 @@ public abstract class PortalGunEntityInWallMixin {
if (entity.level() == null) {
return;
}
for (PortalGunPortalEntity portal : entity.level().getEntitiesOfClass(PortalGunPortalEntity.class, entity.getBoundingBox().inflate(2.0D), PortalGunPortalEntity::isAlive)) {
for (PortalGunPortalEntity portal : com.craisinlord.antarchy.content.portalgun.PortalGunPortalRegistry.near(entity.level(), entity.getBoundingBox().inflate(2.0D))) {
if (portal.intersectsEntityBounds(entity) || portal.getCollisionRemovalAabbForEntity(entity).intersects(entity.getBoundingBox())) {
cir.setReturnValue(false);
return;
@@ -0,0 +1,22 @@
package com.craisinlord.antarchy.mixins;
import com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity;
import net.minecraft.world.entity.projectile.Projectile;
import net.minecraft.world.entity.projectile.ProjectileDeflection;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.HitResult;
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(Projectile.class)
public abstract class PortalGunProjectileHitMixin {
@Inject(method = "hitTargetOrDeflectSelf", at = @At("HEAD"), cancellable = true)
private void antarchy$passThroughPortal(HitResult hit, CallbackInfoReturnable<ProjectileDeflection> cir) {
if (hit.getType() == HitResult.Type.BLOCK && hit instanceof BlockHitResult blockHit
&& PortalGunPortalEntity.passProjectileThroughPortal((Projectile) (Object) this, blockHit)) {
cir.setReturnValue(ProjectileDeflection.NONE);
}
}
}
@@ -0,0 +1,37 @@
package com.craisinlord.antarchy.mixins.client;
import com.craisinlord.antarchy.content.client.PortalGunClientTransit;
import net.minecraft.client.player.LocalPlayer;
import net.minecraft.world.entity.MoverType;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
import org.spongepowered.asm.mixin.Mixin;
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.CallbackInfo;
@Mixin(LocalPlayer.class)
public abstract class LocalPlayerPortalTransitMixin {
@Unique
private AABB antarchy$portalTransitStartBox;
@Unique
private Vec3 antarchy$portalTransitStartVelocity;
@Inject(method = "move", at = @At("HEAD"))
private void antarchy$capturePortalTransitStart(MoverType type, Vec3 movement, CallbackInfo ci) {
this.antarchy$portalTransitStartBox = ((LocalPlayer) (Object) this).getBoundingBox();
this.antarchy$portalTransitStartVelocity = ((LocalPlayer) (Object) this).getDeltaMovement();
}
@Inject(method = "move", at = @At("RETURN"))
private void antarchy$applyPortalTransit(MoverType type, Vec3 movement, CallbackInfo ci) {
AABB startBox = this.antarchy$portalTransitStartBox;
Vec3 startVelocity = this.antarchy$portalTransitStartVelocity;
this.antarchy$portalTransitStartBox = null;
this.antarchy$portalTransitStartVelocity = null;
if (startBox != null && startVelocity != null && type == MoverType.SELF) {
PortalGunClientTransit.afterLocalMove((LocalPlayer) (Object) this, startBox, startVelocity);
}
}
}
@@ -1,6 +1,5 @@
package com.craisinlord.antarchy.mixins.client;
import com.craisinlord.antarchy.content.entity.DorrieEntity;
import com.craisinlord.antarchy.content.entity.HerculesBeetleEntity;
import net.minecraft.client.Camera;
import net.minecraft.world.entity.Entity;
@@ -40,10 +39,5 @@ public abstract class MountedVehicleCameraMixin {
return;
}
// if (vehicle instanceof DorrieEntity) {
// args.set(0, (Float) args.get(0) * 0.25F);
// args.set(1, (Float) args.get(1) + 0.1F);
// args.set(2, (Float) args.get(2) + 0.55F);
// }
}
}
@@ -54,7 +54,7 @@ public abstract class PortalGunEntityTeleportPacketMixin {
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)) {
for (PortalGunPortalEntity portal : com.craisinlord.antarchy.content.portalgun.PortalGunPortalRegistry.near(entity.level(), search)) {
PortalGunWorldPortalShape.PortalLocalCoords coords = portal.getWorldPortalShape().localCoords(probe);
if (coords.depth() >= -entityExtent - 0.5D
&& coords.depth() <= PortalGunWorldPortalShape.DEFAULT_SCAN_DISTANCE + 0.5D
@@ -4,7 +4,6 @@ import com.craisinlord.antarchy.content.client.PortalGunRollClientState;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.ScreenEffectRenderer;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.block.Blocks;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
@@ -16,7 +15,7 @@ public abstract class ScreenEffectRendererPortalMixin {
private static void antarchy$ignorePortalViewBlock(Player player, CallbackInfoReturnable<net.minecraft.world.level.block.state.BlockState> cir) {
Minecraft minecraft = Minecraft.getInstance();
if (player != null && PortalGunRollClientState.isEntityInsidePortal(minecraft, player)) {
cir.setReturnValue(Blocks.AIR.defaultBlockState());
cir.setReturnValue(null);
}
}
}
@@ -0,0 +1,33 @@
package com.craisinlord.antarchy.mixins.gravity;
import com.craisinlord.antarchy.content.gravity.AntarchyGravityApi;
import net.minecraft.core.BlockPos;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.BubbleColumnBlock;
import net.minecraft.world.level.block.state.BlockState;
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(BubbleColumnBlock.class)
public abstract class BubbleColumnGravityMixin {
@Inject(method = "entityInside", at = @At("HEAD"), cancellable = true)
private void antarchy$applyBubbleForces(BlockState state, Level level, BlockPos pos, Entity entity, CallbackInfo ci) {
if (!AntarchyGravityApi.isGravityInverted(entity)) {
return;
}
boolean drag = state.getValue(BubbleColumnBlock.DRAG_DOWN);
Vec3 velocity = AntarchyGravityApi.getWorldVelocity(entity);
double vertical = drag ? Math.max(-0.9D, velocity.y - 0.03D) : Math.min(1.8D, velocity.y + 0.1D);
AntarchyGravityApi.setWorldVelocity(entity, new Vec3(velocity.x, vertical, velocity.z));
entity.onAboveBubbleCol(!drag);
if (drag) {
entity.resetFallDistance();
}
ci.cancel();
}
}
@@ -4,7 +4,6 @@ import com.craisinlord.antarchy.content.fluid.AntarchyFluidChecks;
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.portalgun.PortalGunCollisionHelper;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import java.util.List;
@@ -1,6 +1,8 @@
package com.craisinlord.antarchy.mixins.gravity;
import net.minecraft.network.protocol.game.VecDeltaCodec;
import net.minecraft.server.level.ServerEntity;
import net.minecraft.world.phys.Vec3;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
@@ -9,6 +11,12 @@ import org.spongepowered.asm.mixin.gen.Accessor;
* Accessor for server entity tracking internals.
*/
public interface ServerEntityAccessor {
@Accessor("positionCodec")
VecDeltaCodec antarchy$getPositionCodec();
@Accessor("lastSentMovement")
void antarchy$setLastSentMovement(Vec3 lastSentMovement);
@Accessor("updateInterval")
void antarchy$setUpdateInterval(int updateInterval);
}
@@ -7,7 +7,9 @@
"AbstractMinecartMixin",
"AbstractArrowAccessor",
"PortalGunEntityCollisionMixin",
"PortalGunEntityInWallMixin",
"PortalGunPlayerMovePacketMixin",
"PortalGunProjectileHitMixin",
"ServerGamePacketListenerTeleportAccessor",
"PortalGunZombieMixin",
"FallingBlockEntityAccessor",
@@ -71,6 +73,7 @@
"gravity.AbstractArrowGravityMixin",
"gravity.BehaviorUtilsGravityMixin",
"gravity.BucketItemGravityMixin",
"gravity.BubbleColumnGravityMixin",
"gravity.ChunkMapAccessor",
"gravity.CrossbowItemGravityMixin",
"gravity.DirectionFacingMixin",
@@ -171,6 +174,7 @@
"client.MountedVehicleCameraMixin",
"client.ParalyzedMouseTurnMixin",
"client.LocalPlayerPortalMixin",
"client.LocalPlayerPortalTransitMixin",
"client.PortalGunEntityTeleportPacketMixin",
"client.PortalGunZoomFovMixin",
"client.LivingEntityPortalLerpAccessor",
@@ -671,6 +671,8 @@
"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.portal_gun_atlas": "Atlas Portal Gun",
"item.antarchy.portal_gun_p_body": "P-body Portal Gun",
"item.antarchy.diamond_minecart": "The Diamond Minecart",
"item.antarchy.cogwheel_pet": "Cogwheel Pet",
"item.antarchy.fire_dragon_pet": "Fire Dragon Pet",
@@ -837,13 +839,14 @@
"tooltip.antarchy.gravity_gun.secondary": "Usage: pull in an entity or a valid block.",
"tooltip.antarchy.gravity_gun.scroll": "Hold Ctrl and scroll to move the held target closer or farther away.",
"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.primary": "Attack: fire a %s portal.",
"tooltip.antarchy.portal_gun.secondary": "Use: fire a %s portal.",
"tooltip.antarchy.portal_gun.color.blue": "blue",
"tooltip.antarchy.portal_gun.color.orange": "orange",
"tooltip.antarchy.portal_gun.color.purple": "purple",
"tooltip.antarchy.portal_gun.color.yellow": "yellow",
"tooltip.antarchy.portal_gun.color.red": "red",
"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": " ",
@@ -1250,7 +1253,6 @@
"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",
@@ -0,0 +1,7 @@
{
"parent": "minecraft:item/generated",
"gui_light": "front",
"textures": {
"layer0": "antarchy:item/portal_gun/portal_gun_atlas_item"
}
}
@@ -0,0 +1,7 @@
{
"parent": "minecraft:item/generated",
"gui_light": "front",
"textures": {
"layer0": "antarchy:item/portal_gun/portal_gun_p_body_item"
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 704 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 706 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 705 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 701 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 286 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 341 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 534 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 415 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 416 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 648 B

@@ -0,0 +1,27 @@
{
"type": "minecraft:crafting_shaped",
"category": "equipment",
"pattern": [
"T N",
"TSN",
"TBT"
],
"key": {
"T": {
"item": "antarchy:titanium_ingot"
},
"N": {
"item": "antarchy:nightmare_scale"
},
"S": {
"item": "minecraft:nether_star"
},
"B": {
"item": "antarchy:bluestone_comparator"
}
},
"result": {
"id": "antarchy:portal_gun",
"count": 1
}
}
@@ -0,0 +1,19 @@
{
"type": "minecraft:crafting_shapeless",
"category": "equipment",
"ingredients": [
{
"item": "antarchy:portal_gun"
},
{
"item": "minecraft:light_blue_dye"
},
{
"item": "minecraft:purple_dye"
}
],
"result": {
"id": "antarchy:portal_gun_atlas",
"count": 1
}
}
@@ -0,0 +1,19 @@
{
"type": "minecraft:crafting_shapeless",
"category": "equipment",
"ingredients": [
{
"item": "antarchy:portal_gun"
},
{
"item": "minecraft:yellow_dye"
},
{
"item": "minecraft:red_dye"
}
],
"result": {
"id": "antarchy:portal_gun_p_body",
"count": 1
}
}
@@ -94,13 +94,13 @@
},
{
"type": "antarchy:apple_cow",
"weight": 24,
"weight": 20,
"minCount": 1,
"maxCount": 3
},
{
"type": "antarchy:golden_apple_cow",
"weight": 20,
"weight": 16,
"minCount": 1,
"maxCount": 3
},
@@ -34,6 +34,10 @@
}
},
"spawn_costs": {
"antarchy:basilisk": {
"energy_budget": 0.1,
"charge": 14.0
},
"antarchy:glimmer": {
"energy_budget": 0.1,
"charge": 9
@@ -86,7 +90,14 @@
],
"creature_spawn_probability": 0.07,
"spawners": {
"monster": [],
"monster": [
{
"type": "antarchy:basilisk",
"weight": 10,
"minCount": 1,
"maxCount": 1
}
],
"creature": [
{
"type": "minecraft:frog",
@@ -93,7 +93,7 @@
"monster": [
{
"type": "antarchy:basilisk",
"weight": 20,
"weight": 24,
"minCount": 1,
"maxCount": 1
}
@@ -96,13 +96,13 @@
},
{
"type": "antarchy:apple_cow",
"weight": 24,
"weight": 20,
"minCount": 1,
"maxCount": 3
},
{
"type": "antarchy:golden_apple_cow",
"weight": 20,
"weight": 16,
"minCount": 1,
"maxCount": 3
},
@@ -39,8 +39,8 @@
],
"placement": {
"salt": 659843583,
"spacing": 42,
"separation": 35,
"spacing": 37,
"separation": 30,
"type": "integrated_api:advanced_random_spread"
}
}
@@ -49,7 +49,7 @@
"base": 13,
"per_level_above_first": 8
},
"max_level": 10,
"max_level": 4,
"min_cost": {
"base": 5,
"per_level_above_first": 8
@@ -54,7 +54,7 @@
"base": 18,
"per_level_above_first": 8
},
"max_level": 10,
"max_level": 4,
"min_cost": {
"base": 10,
"per_level_above_first": 8
@@ -37,7 +37,7 @@
"base": 9,
"per_level_above_first": 6
},
"max_level": 10,
"max_level": 4,
"min_cost": {
"base": 3,
"per_level_above_first": 6
@@ -33,7 +33,7 @@
"base": 12,
"per_level_above_first": 11
},
"max_level": 10,
"max_level": 4,
"min_cost": {
"base": 1,
"per_level_above_first": 11
@@ -7,7 +7,6 @@ import com.craisinlord.antarchy.content.entity.ReverieEntity;
import com.craisinlord.antarchy.content.entity.ant.RainbowAntEntity;
import com.craisinlord.antarchy.config.AntarchySettings;
import com.craisinlord.antarchy.content.AntarchyObjects;
import com.craisinlord.antarchy.content.block.DuctTapeBlock;
import com.craisinlord.antarchy.content.item.AttitudeAdjusterItem;
import com.craisinlord.antarchy.content.item.AttitudeAdjusterSlamManager;
import com.craisinlord.antarchy.content.fluid.AntarchyFluidChecks;
@@ -19,7 +18,6 @@ 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;
@@ -78,7 +76,6 @@ 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);
@@ -209,14 +206,13 @@ public final class AntarchyFabricEvents {
});
ServerTickEvents.END_SERVER_TICK.register(server -> {
PortalGunGrabManager.tick(server);
Set<UUID> activeThisTick = new HashSet<>();
for (ServerLevel level : server.getAllLevels()) {
CavarynHordeManager.tick(level);
com.craisinlord.antarchy.content.horde.CavarynCreatureSpawner.tick(level);
ThoraxisUndersideManager.tick(level);
com.craisinlord.antarchy.content.worldgen.elythia.KingsTreeGridFeature.tickPendingSpawns(level);
tickInvertedPlayers(level, activeThisTick);
tickDuctTapePlayers(level);
tickIchorPlayers(level);
tickBloodglassRecharge(level);
AttitudeAdjusterSlamManager.tick(level);
@@ -338,14 +334,6 @@ public final class AntarchyFabricEvents {
}
}
private static void tickDuctTapePlayers(ServerLevel level) {
for (Player player : level.players()) {
if (DuctTapeBlock.shouldTickStuckEntity(player)) {
DuctTapeBlock.tickStuckEntity(player);
}
}
}
private static void tickIchorPlayers(ServerLevel level) {
if (!AntarchySettings.ichorWitherEnabled()) {
return;
@@ -625,15 +625,14 @@ public final class AntarchyFabricClientBootstrap {
DorrieJumpHudRenderer.render(guiGraphics);
HerculesBeetleChargeHudRenderer.render(guiGraphics);
com.craisinlord.antarchy.content.client.KingJudgmentFlashHudRenderer.render(guiGraphics);
com.craisinlord.antarchy.content.client.PortalGunCrosshairRenderer.render(guiGraphics);
});
WorldRenderEvents.LAST.register(context -> {
BloodCrystalKatanaTrailHandler.render(context);
ScorpionWhipTetherRenderHandler.render(context);
WormHookTetherRenderHandler.render(context);
if (com.craisinlord.antarchy.content.client.renderer.PortalGunPortalViewRenderer.isEnabled()) {
com.craisinlord.antarchy.content.client.renderer.PortalGunPortalViewRenderer.render(context.camera(), context.positionMatrix(), context.projectionMatrix(), context.tickCounter());
}
com.craisinlord.antarchy.content.client.renderer.PortalGunPortalViewRenderer.render(context.camera(), context.positionMatrix(), context.projectionMatrix(), context.tickCounter());
});
}
@@ -51,14 +51,8 @@ public final class AntarchyKeyBindings {
);
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,
GLFW.GLFW_KEY_Z,
CATEGORY
);
public static final KeyMapping PORTAL_GUN_RESET = new KeyMapping(
@@ -78,7 +72,6 @@ public final class AntarchyKeyBindings {
KeyBindingHelper.registerKeyBinding(ROYAL_INVERSION_TOGGLE);
KeyBindingHelper.registerKeyBinding(ROYAL_BOUNDARY);
KeyBindingHelper.registerKeyBinding(PORTAL_GUN_ZOOM);
KeyBindingHelper.registerKeyBinding(PORTAL_GUN_GRAB);
KeyBindingHelper.registerKeyBinding(PORTAL_GUN_RESET);
}
@@ -110,10 +103,6 @@ public final class AntarchyKeyBindings {
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();
}
@@ -27,14 +27,12 @@ public final class DorrieJumpClientHandler {
return;
}
// Space bar → upward movement.
boolean pressingJump = mc.options.keyJump.isDown();
if (pressingJump != wasPressingJump) {
ClientPlayNetworking.send(new DorrieJumpInputPayload(pressingJump));
wasPressingJump = pressingJump;
}
// Left ctrl → charge jump.
boolean pressingCharge = AntarchyKeyBindings.isMountSpecialPressed();
if (pressingCharge != wasPressingCharge) {
ClientPlayNetworking.send(new DorrieChargeJumpPayload(pressingCharge));
@@ -3,7 +3,6 @@ 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;
@@ -20,23 +19,19 @@ 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);
PortalGunZoomClientState.tick(mc, held, 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;
@@ -20,6 +20,8 @@ public final class SeparateLargeItemModels {
entry(AntarchyFabricItems.GROWTH_RAY.get(), "grow_ray_gui"),
entry(AntarchyFabricItems.KRAKENS_GRASP.get(), "krakens_grasp_gui"),
entry(AntarchyFabricItems.PORTAL_GUN.get(), "portal_gun_gui"),
entry(AntarchyFabricItems.PORTAL_GUN_ATLAS.get(), "portal_gun_atlas_gui"),
entry(AntarchyFabricItems.PORTAL_GUN_P_BODY.get(), "portal_gun_p_body_gui"),
entry(AntarchyFabricItems.ROYAL_ASSAILANT_BATTLEAXE.get(), "royal_assailant_battleaxe_gui"),
entry(AntarchyFabricItems.ROYAL_ASSAILANT_STAFF.get(), "royal_assailant_staff_gui"),
entry(AntarchyFabricItems.ROYAL_GUARDIAN_SWORD.get(), "royal_guardian_sword_gui"),
@@ -1,30 +0,0 @@
package com.craisinlord.antarchy.fabric.mixin.client;
import com.craisinlord.antarchy.content.block.DuctTapeBlock;
import net.minecraft.client.Minecraft;
import net.minecraft.client.player.Input;
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(Input.class)
public abstract class DuctTapeInputMixin {
@Inject(method = "tick", at = @At("RETURN"))
private void blockInputInDuctTape(boolean isSneaking, float slowFactor, CallbackInfo ci) {
Minecraft mc = Minecraft.getInstance();
if (mc.player == null || mc.level == null) return;
if (!mc.player.isNoGravity() && !DuctTapeBlock.isTouchingTape(mc.level, mc.player)) return;
Input self = (Input) (Object) this;
self.left = false;
self.right = false;
self.up = false;
self.down = false;
self.leftImpulse = 0.0F;
self.forwardImpulse = 0.0F;
self.jumping = false;
self.shiftKeyDown = false;
}
}
@@ -0,0 +1,28 @@
package com.craisinlord.antarchy.fabric.mixin.client;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import com.mojang.blaze3d.pipeline.RenderTarget;
import java.nio.IntBuffer;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL30;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
@Mixin(RenderTarget.class)
public abstract class RenderTargetDepthStencilMixin {
@WrapOperation(method = "createBuffers", at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/platform/GlStateManager;_texImage2D(IIIIIIIILjava/nio/IntBuffer;)V"))
private void antarchy$allocateDepthStencil(int target, int level, int internalFormat, int width, int height, int border, int format, int type,
IntBuffer pixels, Operation<Void> original) {
if (internalFormat == GL11.GL_DEPTH_COMPONENT) {
original.call(target, level, GL30.GL_DEPTH24_STENCIL8, width, height, border, GL30.GL_DEPTH_STENCIL, GL30.GL_UNSIGNED_INT_24_8, pixels);
return;
}
original.call(target, level, internalFormat, width, height, border, format, type, pixels);
}
@WrapOperation(method = "createBuffers", at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/platform/GlStateManager;_glFramebufferTexture2D(IIIII)V"))
private void antarchy$attachDepthStencil(int target, int attachment, int textureTarget, int texture, int level, Operation<Void> original) {
original.call(target, attachment == GL30.GL_DEPTH_ATTACHMENT ? GL30.GL_DEPTH_STENCIL_ATTACHMENT : attachment, textureTarget, texture, level);
}
}
@@ -45,8 +45,6 @@ public final class AntarchyFabricClientNetworking {
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) ->
@@ -50,7 +50,6 @@ public final class AntarchyFabricNetworking {
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() {
@@ -76,7 +75,6 @@ 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);
@@ -92,8 +90,8 @@ public final class AntarchyFabricNetworking {
PayloadTypeRegistry.playC2S().register(GravityGunPrimaryPayload.TYPE, GravityGunPrimaryPayload.STREAM_CODEC);
PayloadTypeRegistry.playC2S().register(PortalGunPrimaryPayload.TYPE, PortalGunPrimaryPayload.STREAM_CODEC);
PayloadTypeRegistry.playC2S().register(com.craisinlord.antarchy.content.network.PortalGunTransitPayload.TYPE, com.craisinlord.antarchy.content.network.PortalGunTransitPayload.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);
@@ -128,10 +126,15 @@ 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(com.craisinlord.antarchy.content.network.PortalGunTransitPayload.TYPE, (payload, context) -> {
if (context.server().isSameThread()) {
com.craisinlord.antarchy.content.portalgun.PortalGunTransitHandler.handle(context.player(), payload);
} else {
context.server().execute(() -> com.craisinlord.antarchy.content.portalgun.PortalGunTransitHandler.handle(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) ->
@@ -1561,7 +1561,28 @@ public final class AntarchyFabricItems {
AntarchyFabricEntities.PORTAL_GUN_BLACK_HOLE,
AntarchyFabricEntities.PORTAL_GUN_PROJECTILE,
AntarchyFabricBlocks.PORTAL_GUN_PORTAL_MASTER,
AntarchyFabricBlocks.PORTAL_GUN_PORTAL_BASE
AntarchyFabricBlocks.PORTAL_GUN_PORTAL_BASE,
com.craisinlord.antarchy.content.portalgun.PortalGunVariant.DEFAULT
));
public static final DeferredItem<PortalGunItem> PORTAL_GUN_ATLAS = ITEMS.register("portal_gun_atlas",
() -> new PortalGunItem(
new Item.Properties().stacksTo(1).durability(256).rarity(Rarity.RARE),
AntarchyFabricEntities.PORTAL_GUN_PORTAL,
AntarchyFabricEntities.PORTAL_GUN_BLACK_HOLE,
AntarchyFabricEntities.PORTAL_GUN_PROJECTILE,
AntarchyFabricBlocks.PORTAL_GUN_PORTAL_MASTER,
AntarchyFabricBlocks.PORTAL_GUN_PORTAL_BASE,
com.craisinlord.antarchy.content.portalgun.PortalGunVariant.ATLAS
));
public static final DeferredItem<PortalGunItem> PORTAL_GUN_P_BODY = ITEMS.register("portal_gun_p_body",
() -> new PortalGunItem(
new Item.Properties().stacksTo(1).durability(256).rarity(Rarity.RARE),
AntarchyFabricEntities.PORTAL_GUN_PORTAL,
AntarchyFabricEntities.PORTAL_GUN_BLACK_HOLE,
AntarchyFabricEntities.PORTAL_GUN_PROJECTILE,
AntarchyFabricBlocks.PORTAL_GUN_PORTAL_MASTER,
AntarchyFabricBlocks.PORTAL_GUN_PORTAL_BASE,
com.craisinlord.antarchy.content.portalgun.PortalGunVariant.P_BODY
));
public static final DeferredItem<EyeOfTheStormItem> EYE_OF_THE_STORM = ITEMS.register("eye_of_the_storm",
@@ -27,12 +27,12 @@
"infinity.AmendmentRareOresMixin"
],
"client": [
"client.RenderTargetDepthStencilMixin",
"client.SeparateLargeItemModelsMixin",
"client.SeparateLargeItemGuiLightingMixin",
"client.CameraShakeMixin",
"client.BrutalflyElytraExperienceBarMixin",
"client.BloodglassChatOrderMixin",
"client.DuctTapeInputMixin",
"client.LiquidFogMixin",
"client.GravityGunMouseScrollMixin",
"client.KeybindingConflictFixMixin",
@@ -0,0 +1,3 @@
{
"parent": "antarchy:item/portal_gun_held_geo"
}
@@ -0,0 +1,3 @@
{
"parent": "antarchy:item/portal_gun_held_geo"
}
View File
Whitespace-only changes.
@@ -671,8 +671,9 @@ public final class AntarchyConfigModuleNeoforge {
AntarchySettings.setPortalGunSeeThroughPortals(AntarchyMiscConfig.portalGunSeeThroughPortals());
AntarchySettings.setPortalGunFancyPortals(AntarchyMiscConfig.portalGunFancyPortals());
AntarchySettings.setPortalGunMaxShootDistance(AntarchyMiscConfig.portalGunMaxShootDistance());
AntarchySettings.setPortalGunEntityGrabWeightBase(AntarchyMiscConfig.portalGunEntityGrabWeightBase());
AntarchySettings.setPortalGunIndicatorSize(AntarchyMiscConfig.portalGunIndicatorSize());
AntarchySettings.setPortalGunMinFloorExitSpeed(AntarchyMiscConfig.portalGunMinFloorExitSpeed());
AntarchySettings.setPortalGunMaxLiveViews(AntarchyMiscConfig.portalGunMaxLiveViews());
AntarchySettings.setPortalGunMaxExitSpeed(AntarchyMiscConfig.portalGunMaxExitSpeed());
// Misc
@@ -39,8 +39,9 @@ public final class AntarchyMiscConfig {
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;
private static final ModConfigSpec.DoubleValue PORTAL_GUN_MIN_FLOOR_EXIT_SPEED;
private static final ModConfigSpec.IntValue PORTAL_GUN_MAX_LIVE_VIEWS;
private static final ModConfigSpec.DoubleValue PORTAL_GUN_MAX_EXIT_SPEED;
// Diamond Minecart
private static final ModConfigSpec.BooleanValue DIAMOND_MINECART_ENABLED;
@@ -146,8 +147,9 @@ public final class AntarchyMiscConfig {
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);
PORTAL_GUN_MAX_LIVE_VIEWS = b.comment("How many portals can show a live see-through view at once. Each one re-renders the world, so higher values cost frame rate. 0 shows only the animated surface.").defineInRange("maxLiveViews", 8, 0, 32);
PORTAL_GUN_MIN_FLOOR_EXIT_SPEED = b.comment("Minimum speed in blocks/tick when leaving an upward-facing portal, so the traveller clears it. 0 disables.").defineInRange("minFloorExitSpeed", 0.6D, 0.0D, 4.0D);
PORTAL_GUN_MAX_EXIT_SPEED = b.comment("Maximum speed in blocks/tick when leaving a portal. 0 keeps momentum uncapped.").defineInRange("maxExitSpeed", 0.0D, 0.0D, 100.0D);
b.pop();
@@ -224,8 +226,9 @@ public final class AntarchyMiscConfig {
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(); }
public static double portalGunMinFloorExitSpeed() { return PORTAL_GUN_MIN_FLOOR_EXIT_SPEED.get(); }
public static int portalGunMaxLiveViews() { return PORTAL_GUN_MAX_LIVE_VIEWS.get(); }
public static double portalGunMaxExitSpeed() { return PORTAL_GUN_MAX_EXIT_SPEED.get(); }
static boolean diamondMinecartEnabled() { return DIAMOND_MINECART_ENABLED.get(); }
static boolean diamondMinecartPlacesRails() { return DIAMOND_MINECART_PLACES_RAILS.get(); }
@@ -65,13 +65,6 @@ public final class AntarchyKeyBindings {
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,
@@ -91,7 +84,6 @@ public final class AntarchyKeyBindings {
event.register(ROYAL_INVERSION_TOGGLE);
event.register(ROYAL_BOUNDARY);
event.register(PORTAL_GUN_ZOOM);
event.register(PORTAL_GUN_GRAB);
event.register(PORTAL_GUN_RESET);
}
@@ -119,10 +111,6 @@ public final class AntarchyKeyBindings {
return PORTAL_GUN_ZOOM.consumeClick();
}
public static boolean consumePortalGunGrabPressed() {
return PORTAL_GUN_GRAB.consumeClick();
}
public static boolean consumePortalGunResetPressed() {
return PORTAL_GUN_RESET.consumeClick();
}
Loaded 100 of 111 files, more files were not shown because too many files have changed in this diff. Show more