performance improvements

Craisin portal gun variant
This commit is contained in:
craisinlord committed 2026-10-08 16:51:36 -05:00
1 parent 216efeb39b
commit db3cd02059
40 files changed
+530 -64

No files matched your search

@@ -5,6 +5,7 @@ import com.craisinlord.antarchy.content.entity.portal.DimensionalTearEntity;
import java.util.function.Supplier;
import net.minecraft.core.BlockPos;
import net.minecraft.core.HolderLookup;
import net.minecraft.core.SectionPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.util.Mth;
@@ -12,6 +13,7 @@ import net.minecraft.util.RandomSource;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.Nullable;
@@ -139,14 +141,21 @@ public class DimensionalTearMarkerBlockEntity extends BlockEntity {
if (pos.getY() < minY || pos.getY() > maxY) {
return false;
}
BlockPos.MutableBlockPos checkPos = new BlockPos.MutableBlockPos();
LevelChunk chunk = null;
for (int dx = -2; dx <= 2; dx++) {
for (int dy = -2; dy <= 3; dy++) {
for (int dz = -4; dz <= 4; dz++) {
BlockPos checkPos = pos.offset(dx, dy, dz);
if (!level.isLoaded(checkPos)) {
for (int dz = -4; dz <= 4; dz++) {
int chunkX = SectionPos.blockToSectionCoord(pos.getX() + dx);
int chunkZ = SectionPos.blockToSectionCoord(pos.getZ() + dz);
if (chunk == null || chunk.getPos().x != chunkX || chunk.getPos().z != chunkZ) {
chunk = level.getChunkSource().getChunkNow(chunkX, chunkZ);
if (chunk == null) {
return false;
}
if (!level.getBlockState(checkPos).getCollisionShape(level, checkPos).isEmpty()) {
}
for (int dy = -2; dy <= 3; dy++) {
checkPos.set(pos.getX() + dx, pos.getY() + dy, pos.getZ() + dz);
if (!chunk.getBlockState(checkPos).getCollisionShape(level, checkPos).isEmpty()) {
return false;
}
}
@@ -275,7 +275,7 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity implements Por
Vec3 center = placement.center();
restored.moveTo(center.x, center.y, center.z, placement.yaw(), 0.0F);
level.addFreshEntity(restored);
PortalGunSavedData.setPortal(level, this.gunId, this.side, restored.getUUID());
PortalGunSavedData.setPortal(level, this.gunId, this.ownerId, this.side, restored.getUUID());
return restored;
}
@@ -300,7 +300,7 @@ public class PortalGunPortalMasterBlockEntity extends BlockEntity implements Por
Vec3 center = placement.center();
restored.moveTo(center.x, center.y, center.z, placement.yaw(), 0.0F);
level.addFreshEntity(restored);
PortalGunSavedData.setPortal(level, record.gunId(), record.side(), restored.getUUID());
PortalGunSavedData.setPortal(level, record.gunId(), record.ownerId(), 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);
@@ -0,0 +1,49 @@
package com.craisinlord.antarchy.content.command;
import com.craisinlord.antarchy.content.portalgun.PortalGunSavedData;
import com.mojang.authlib.GameProfile;
import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.Commands;
import net.minecraft.commands.arguments.GameProfileArgument;
import net.minecraft.network.chat.Component;
public final class PortalGunCommand {
private PortalGunCommand() {
}
public static void register(CommandDispatcher<CommandSourceStack> dispatcher) {
dispatcher.register(
Commands.literal("portalgun")
.requires(source -> source.hasPermission(2))
.then(Commands.literal("clear")
.then(Commands.literal("all")
.executes(PortalGunCommand::clearAll))
.then(Commands.argument("owners", GameProfileArgument.gameProfile())
.executes(PortalGunCommand::clearOwners)))
);
}
private static int clearAll(CommandContext<CommandSourceStack> context) {
int cleared = PortalGunSavedData.clearEverything(context.getSource().getServer());
context.getSource().sendSuccess(() -> Component.translatable("commands.antarchy.portalgun.clear.all", cleared), true);
return cleared;
}
private static int clearOwners(CommandContext<CommandSourceStack> context) throws CommandSyntaxException {
Collection<GameProfile> profiles = GameProfileArgument.getGameProfiles(context, "owners");
Set<UUID> ownerIds = new HashSet<>();
for (GameProfile profile : profiles) {
ownerIds.add(profile.getId());
}
int cleared = PortalGunSavedData.clearOwnedBy(context.getSource().getServer(), ownerIds);
context.getSource().sendSuccess(() -> Component.translatable("commands.antarchy.portalgun.clear.owners", cleared, profiles.size()), true);
return cleared;
}
}
@@ -428,6 +428,7 @@ public final class CreativeTabContents {
entry("portal_gun", "combat"),
entry("portal_gun_atlas", "combat"),
entry("portal_gun_p_body", "combat"),
entry("portal_gun_craisin", "combat"),
entry("eye_of_the_storm", "combat"),
entry("water_cannon", "combat"),
entry("attitude_adjuster", "combat"),
@@ -128,6 +128,11 @@ public class BedBugEntity extends Animal implements GeoEntity {
&& Animal.checkAnimalSpawnRules(entityType, level, spawnReason, pos, random);
}
@Override
public boolean removeWhenFarAway(double distanceToClosestPlayer) {
return true;
}
@Override
protected void registerGoals() {
this.goalSelector.addGoal(0, new FloatGoal(this));
@@ -0,0 +1,94 @@
package com.craisinlord.antarchy.content.entity.ant;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import net.minecraft.core.BlockPos;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerChunkCache;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.level.TicketType;
import net.minecraft.world.level.ChunkPos;
public final class AntArrivalScheduler {
private static final TicketType<ChunkPos> ARRIVAL_TICKET = TicketType.create("antarchy_ant_arrival", Comparator.comparingLong(ChunkPos::toLong), 300);
private static final int MAX_WAIT_TICKS = 200;
private static final Map<UUID, PendingArrival> PENDING = new LinkedHashMap<>();
private AntArrivalScheduler() {
}
static void request(ServerPlayer player, ServerLevel destination, BlockPos center, int searchRadius, Runnable onLoaded) {
ChunkPos centerChunk = new ChunkPos(center);
int chunkRadius = (searchRadius >> 4) + 1;
destination.getChunkSource().addRegionTicket(ARRIVAL_TICKET, centerChunk, chunkRadius + 1, centerChunk);
PendingArrival arrival = new PendingArrival(player, destination, centerChunk, chunkRadius, onLoaded);
if (arrival.isLoaded()) {
PENDING.remove(player.getUUID());
onLoaded.run();
return;
}
PENDING.put(player.getUUID(), arrival);
}
public static void tick(MinecraftServer server) {
if (PENDING.isEmpty()) {
return;
}
List<PendingArrival> ready = new ArrayList<>();
Iterator<PendingArrival> iterator = PENDING.values().iterator();
while (iterator.hasNext()) {
PendingArrival arrival = iterator.next();
if (arrival.destination.getServer() != server || arrival.player.hasDisconnected() || arrival.player.isRemoved()) {
iterator.remove();
continue;
}
arrival.waitedTicks++;
if (arrival.waitedTicks >= MAX_WAIT_TICKS || arrival.isLoaded()) {
iterator.remove();
ready.add(arrival);
}
}
for (PendingArrival arrival : ready) {
arrival.onLoaded.run();
}
}
public static void clear() {
PENDING.clear();
}
private static final class PendingArrival {
private final ServerPlayer player;
private final ServerLevel destination;
private final ChunkPos center;
private final int chunkRadius;
private final Runnable onLoaded;
private int waitedTicks;
private PendingArrival(ServerPlayer player, ServerLevel destination, ChunkPos center, int chunkRadius, Runnable onLoaded) {
this.player = player;
this.destination = destination;
this.center = center;
this.chunkRadius = chunkRadius;
this.onLoaded = onLoaded;
}
private boolean isLoaded() {
ServerChunkCache chunks = this.destination.getChunkSource();
for (int chunkX = this.center.x - this.chunkRadius; chunkX <= this.center.x + this.chunkRadius; chunkX++) {
for (int chunkZ = this.center.z - this.chunkRadius; chunkZ <= this.center.z + this.chunkRadius; chunkZ++) {
if (chunks.getChunkNow(chunkX, chunkZ) == null) {
return false;
}
}
}
return true;
}
}
}
@@ -6,6 +6,7 @@ import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Consumer;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.nbt.CompoundTag;
@@ -114,23 +115,13 @@ public final class AntTeleportHelper {
return InteractionResult.PASS;
}
Vec3 destinationPos = getDestinationPosition(player, destination);
if (destinationPos == null) {
return failArrival(player);
}
teleportPlayerWithCompanions(player, destination, destinationPos);
player.playSound(SoundEvents.ENDERMAN_TELEPORT, 1.0F, 1.0F);
arriveWithCompanions(player, destination);
return InteractionResult.CONSUME;
}
public static InteractionResult teleportPlayerToReturnDestination(ServerPlayer player) {
ServerLevel destination = resolveReturnDestinationLevel(player);
Vec3 destinationPos = getDestinationPosition(player, destination);
if (destinationPos == null) {
return failArrival(player);
}
teleportPlayerWithCompanions(player, destination, destinationPos);
player.playSound(SoundEvents.ENDERMAN_TELEPORT, 1.0F, 1.0F);
arriveWithCompanions(player, destination);
return InteractionResult.CONSUME;
}
@@ -167,12 +158,7 @@ public final class AntTeleportHelper {
return InteractionResult.PASS;
}
Vec3 destinationPos = getDestinationPosition(player, destination);
if (destinationPos == null) {
return failArrival(player);
}
teleportPlayerWithCompanions(player, destination, destinationPos);
player.playSound(SoundEvents.ENDERMAN_TELEPORT, 1.0F, 1.0F);
arriveWithCompanions(player, destination);
return InteractionResult.CONSUME;
}
@@ -246,6 +232,42 @@ public final class AntTeleportHelper {
return InteractionResult.CONSUME;
}
private static void arriveWithCompanions(ServerPlayer player, ServerLevel destination) {
prepareArrival(player, destination, destinationPos -> {
if (destinationPos == null) {
failArrival(player);
return;
}
teleportPlayerWithCompanions(player, destination, destinationPos);
player.playSound(SoundEvents.ENDERMAN_TELEPORT, 1.0F, 1.0F);
});
}
public static void prepareArrival(ServerPlayer player, ServerLevel destination, Consumer<Vec3> onArrival) {
BlockPos preferredPos = preferredArrivalPos(player, destination);
AntArrivalScheduler.request(player, destination, preferredPos, ARRIVAL_SEARCH_RADIUS, () -> {
int[] yRange = getDimensionYRange(destination);
if (yRange == null) {
onArrival.accept(findSafeArrivalPositionAnywhere(player, destination, preferredPos));
return;
}
Vec3 safePos = findSafeArrivalPositionInYRange(
player, destination, preferredPos, yRange[0], yRange[1], ARRIVAL_SEARCH_RADIUS, ARRIVAL_VERTICAL_SEARCH);
if (safePos != null) {
onArrival.accept(safePos);
return;
}
AntArrivalScheduler.request(player, destination, preferredPos, EXPANDED_ARRIVAL_SEARCH_RADIUS, () -> {
Vec3 expandedSafePos = findSafeArrivalPositionInYRange(
player, destination, preferredPos, yRange[0], yRange[1],
EXPANDED_ARRIVAL_SEARCH_RADIUS, EXPANDED_ARRIVAL_VERTICAL_SEARCH);
onArrival.accept(expandedSafePos != null
? expandedSafePos
: createEmergencyArrivalPlatform(destination, preferredPos, yRange[0], yRange[1]));
});
});
}
public static ServerLevel resolveReturnDestinationLevel(ServerPlayer player) {
ResourceKey<Level> respawnDimension = player.getRespawnDimension();
ServerLevel destination = player.server.getLevel(respawnDimension);
@@ -258,16 +280,15 @@ public final class AntTeleportHelper {
@Nullable
public static Vec3 getDestinationPosition(ServerPlayer player, ServerLevel destination) {
BlockPos respawnPos = player.getRespawnPosition();
BlockPos preferredPos;
if (respawnPos != null && destination.dimension() == player.getRespawnDimension()) {
preferredPos = respawnPos;
} else {
preferredPos = destination.dimension() == Level.END ? ServerLevel.END_SPAWN_POINT : destination.getSharedSpawnPos();
}
return findSafeArrivalPosition(player, destination, preferredArrivalPos(player, destination));
}
Vec3 safeArrivalPos = findSafeArrivalPosition(player, destination, preferredPos);
return safeArrivalPos;
private static BlockPos preferredArrivalPos(ServerPlayer player, ServerLevel destination) {
BlockPos respawnPos = player.getRespawnPosition();
if (respawnPos != null && destination.dimension() == player.getRespawnDimension()) {
return respawnPos;
}
return destination.dimension() == Level.END ? ServerLevel.END_SPAWN_POINT : destination.getSharedSpawnPos();
}
@Nullable
@@ -290,6 +311,11 @@ public final class AntTeleportHelper {
return expandedSafePos != null ? expandedSafePos : createEmergencyArrivalPlatform(destination, preferredPos, yRange[0], yRange[1]);
}
return findSafeArrivalPositionAnywhere(player, destination, preferredPos);
}
@Nullable
private static Vec3 findSafeArrivalPositionAnywhere(ServerPlayer player, ServerLevel destination, BlockPos preferredPos) {
Set<BlockPos> candidates = new LinkedHashSet<>();
BlockPos adjustedPreferredPos = player.adjustSpawnLocation(destination, preferredPos);
addArrivalCandidate(candidates, preferredPos);
@@ -122,13 +122,14 @@ public class RainbowAntEntity extends BaseAntEntity implements GeoEntity {
if (this.isInInfinityDimension(serverPlayer.serverLevel())) {
ServerLevel returnLevel = this.resolveReturnDestinationLevel(serverPlayer);
Vec3 returnPos = this.getDestinationPosition(returnLevel, serverPlayer);
if (returnPos == null) {
serverPlayer.displayClientMessage(Component.translatable("message.antarchy.teleport_arrival_failed"), true);
return InteractionResult.CONSUME;
}
serverPlayer.teleportTo(returnLevel, returnPos.x, returnPos.y, returnPos.z, serverPlayer.getYRot(), serverPlayer.getXRot());
serverPlayer.playSound(SoundEvents.ENDERMAN_TELEPORT, 1.0F, 1.0F);
AntTeleportHelper.prepareArrival(serverPlayer, returnLevel, returnPos -> {
if (returnPos == null) {
serverPlayer.displayClientMessage(Component.translatable("message.antarchy.teleport_arrival_failed"), true);
return;
}
serverPlayer.teleportTo(returnLevel, returnPos.x, returnPos.y, returnPos.z, serverPlayer.getYRot(), serverPlayer.getXRot());
serverPlayer.playSound(SoundEvents.ENDERMAN_TELEPORT, 1.0F, 1.0F);
});
return InteractionResult.CONSUME;
}
@@ -118,6 +118,7 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
private static final double BEAM_MUZZLE_CENTER_Y = 11.5D;
private static final double BEAM_MUZZLE_SIDE_Y = 10.25D;
private static final double BEAM_MUZZLE_LATERAL = 2.625D;
private static final int HEAVY_BLOCK_CHECK_INTERVAL_TICKS = 4;
protected static final double FLYING_MIN_HOVER = 2.5D;
protected static final double FLYING_PREFERRED_HOVER = 6.0D;
@@ -464,6 +465,36 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
}
}
private final java.util.Map<net.minecraft.tags.TagKey<net.minecraft.world.level.material.Fluid>, CachedFluidCheck> cachedFluidChecks = new java.util.HashMap<>(2);
public boolean shouldRunHeavyBlockChecks() {
return (this.tickCount + this.getId()) % HEAVY_BLOCK_CHECK_INTERVAL_TICKS == 0;
}
@Override
protected void tryCheckInsideBlocks() {
if (this.shouldRunHeavyBlockChecks()) {
super.tryCheckInsideBlocks();
}
}
@Override
public boolean updateFluidHeightAndDoFluidPushing(net.minecraft.tags.TagKey<net.minecraft.world.level.material.Fluid> fluidTag, double motionScale) {
CachedFluidCheck cached = this.cachedFluidChecks.get(fluidTag);
if (cached == null || this.shouldRunHeavyBlockChecks()) {
boolean inFluid = super.updateFluidHeightAndDoFluidPushing(fluidTag, motionScale);
this.cachedFluidChecks.put(fluidTag, new CachedFluidCheck(inFluid, this.getFluidHeight(fluidTag)));
return inFluid;
}
if (cached.height() > 0.0D) {
this.fluidHeight.put(fluidTag, cached.height());
}
return cached.inFluid();
}
private record CachedFluidCheck(boolean inFluid, double height) {
}
@Override
public void travel(Vec3 travelVector) {
if (this.isRoyalFlying() && this.isEffectiveAi()) {
@@ -150,6 +150,24 @@ public class PortalGunItem extends Item implements GeoItem {
player.serverLevel().playSound(null, player.getX(), player.getY(), player.getZ(), PortalGunPortalEntity.sound("portal_fizzle"), SoundSource.PLAYERS, 0.6F, 1.0F);
}
private boolean damageGun(ServerLevel level, Player player, ItemStack stack, boolean offhand) {
UUID gunId = getGunId(stack);
EquipmentSlot slot = offhand ? EquipmentSlot.OFFHAND : EquipmentSlot.MAINHAND;
boolean[] broke = {false};
stack.hurtAndBreak(1, level, player instanceof ServerPlayer serverPlayer ? serverPlayer : null, item -> {
broke[0] = true;
player.onEquippedItemBroken(item, slot);
PortalGunSavedData.clearAllPortals(level, gunId);
});
return broke[0];
}
public static void clearPortalsOf(ServerLevel level, ItemStack stack) {
if (stack.getItem() instanceof PortalGunItem) {
PortalGunSavedData.clearAllPortals(level, getGunId(stack));
}
}
public void fireMobPortal(ServerLevel level, LivingEntity shooter, ItemStack stack, PortalGunPortalEntity.PortalSide side) {
this.ensureGunId(stack, shooter.getUUID());
String firePath = side == PortalGunPortalEntity.PortalSide.BLUE ? "portal_gun_fire_blue" : "portal_gun_fire_orange";
@@ -165,7 +183,9 @@ public class PortalGunItem extends Item implements GeoItem {
}
this.triggerFireAnimation(level, player, stack);
this.ensureGunId(stack, player.getUUID());
stack.hurtAndBreak(1, player, offhand ? EquipmentSlot.OFFHAND : EquipmentSlot.MAINHAND);
if (this.damageGun(level, player, stack, offhand)) {
return true;
}
player.awardStat(Stats.ITEM_USED.get(this));
player.getCooldowns().addCooldown(this, 6);
String firePath = side == PortalGunPortalEntity.PortalSide.BLUE ? "portal_gun_fire_blue" : "portal_gun_fire_orange";
@@ -211,7 +231,7 @@ public class PortalGunItem extends Item implements GeoItem {
return;
}
level.addFreshEntity(portal);
PortalGunSavedData.setPortal(level, gunId, side, portal.getUUID());
PortalGunSavedData.setPortal(level, gunId, portalOwnerId, side, portal.getUUID());
PortalGunPortalEntity other = this.findCounterpart(level, gunId, side);
if (other != null) {
portal.linkTo(other);
@@ -267,7 +287,9 @@ public class PortalGunItem extends Item implements GeoItem {
this.setMoonSide(stack, side);
this.setLastSide(stack, side);
this.triggerFireAnimation(level, player, stack);
stack.hurtAndBreak(1, player, offhand ? EquipmentSlot.OFFHAND : EquipmentSlot.MAINHAND);
if (this.damageGun(level, player, stack, offhand)) {
return true;
}
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);
@@ -33,13 +33,14 @@ public class RainbowSugarItem extends Item {
if (!level.isClientSide && livingEntity instanceof ServerPlayer serverPlayer && isInInfinityDimension(serverPlayer.serverLevel())) {
ServerLevel returnLevel = AntTeleportHelper.resolveReturnDestinationLevel(serverPlayer);
var returnPos = AntTeleportHelper.getDestinationPosition(serverPlayer, returnLevel);
if (returnPos == null) {
serverPlayer.displayClientMessage(Component.translatable("message.antarchy.teleport_arrival_failed"), true);
} else {
serverPlayer.teleportTo(returnLevel, returnPos.x, returnPos.y, returnPos.z, serverPlayer.getYRot(), serverPlayer.getXRot());
serverPlayer.playSound(SoundEvents.ENDERMAN_TELEPORT, 1.0F, 1.0F);
}
AntTeleportHelper.prepareArrival(serverPlayer, returnLevel, returnPos -> {
if (returnPos == null) {
serverPlayer.displayClientMessage(Component.translatable("message.antarchy.teleport_arrival_failed"), true);
} else {
serverPlayer.teleportTo(returnLevel, returnPos.x, returnPos.y, returnPos.z, serverPlayer.getYRot(), serverPlayer.getXRot());
serverPlayer.playSound(SoundEvents.ENDERMAN_TELEPORT, 1.0F, 1.0F);
}
});
}
return result;
@@ -22,6 +22,7 @@ public final class PortalGunSavedData extends SavedData {
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 OWNER_KEY = "Owner";
private final Map<PairKey, PairRecord> pairs = new HashMap<>();
public static PortalGunSavedData create() {
@@ -38,7 +39,8 @@ public final class PortalGunSavedData extends SavedData {
}
UUID blue = entryTag.hasUUID(BLUE_KEY) ? entryTag.getUUID(BLUE_KEY) : null;
UUID orange = entryTag.hasUUID(ORANGE_KEY) ? entryTag.getUUID(ORANGE_KEY) : null;
data.pairs.put(new PairKey(dimension, entryTag.getUUID(GUN_KEY)), new PairRecord(blue, orange));
UUID owner = entryTag.hasUUID(OWNER_KEY) ? entryTag.getUUID(OWNER_KEY) : null;
data.pairs.put(new PairKey(dimension, entryTag.getUUID(GUN_KEY)), new PairRecord(blue, orange, owner));
}
return data;
}
@@ -56,6 +58,9 @@ public final class PortalGunSavedData extends SavedData {
if (entry.getValue().orangePortalId() != null) {
entryTag.putUUID(ORANGE_KEY, entry.getValue().orangePortalId());
}
if (entry.getValue().ownerId() != null) {
entryTag.putUUID(OWNER_KEY, entry.getValue().ownerId());
}
entries.add(entryTag);
}
tag.put(ENTRIES_KEY, entries);
@@ -84,16 +89,17 @@ public final class PortalGunSavedData extends SavedData {
return portalId != null && getPortalId(level.getServer(), gunId, side, level.dimension().location()).map(portalId::equals).orElse(false);
}
public static void setPortal(ServerLevel level, UUID gunId, PortalGunPortalEntity.PortalSide side, UUID portalId) {
public static void setPortal(ServerLevel level, UUID gunId, UUID ownerId, PortalGunPortalEntity.PortalSide side, UUID portalId) {
if (gunId == null) {
return;
}
PortalGunSavedData data = get(level.getServer());
PairKey key = new PairKey(level.dimension().location(), gunId);
PairRecord current = data.pairs.getOrDefault(key, new PairRecord(null, null));
PairRecord current = data.pairs.getOrDefault(key, new PairRecord(null, null, null));
UUID owner = ownerId != null ? ownerId : current.ownerId();
data.put(key, side == PortalGunPortalEntity.PortalSide.BLUE
? new PairRecord(portalId, current.orangePortalId())
: new PairRecord(current.bluePortalId(), portalId));
? new PairRecord(portalId, current.orangePortalId(), owner)
: new PairRecord(current.bluePortalId(), portalId, owner));
}
public static void clearPortal(MinecraftServer server, UUID gunId, PortalGunPortalEntity.PortalSide side, UUID portalId, ResourceLocation dimension) {
@@ -108,7 +114,7 @@ public final class PortalGunSavedData extends SavedData {
}
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));
data.put(key, new PairRecord(blue, orange, current.ownerId()));
}
public static void clearAllPortals(ServerLevel level, UUID gunId) {
@@ -149,14 +155,41 @@ public final class PortalGunSavedData extends SavedData {
}
portalIds.add(removed);
data.put(entry.getKey(), side == PortalGunPortalEntity.PortalSide.BLUE
? new PairRecord(null, pair.orangePortalId())
: new PairRecord(pair.bluePortalId(), null));
? new PairRecord(null, pair.orangePortalId(), pair.ownerId())
: new PairRecord(pair.bluePortalId(), null, pair.ownerId()));
}
for (UUID portalId : portalIds) {
discardIfLoaded(server, portalId);
}
}
public static int clearOwnedBy(MinecraftServer server, Set<UUID> ownerIds) {
return clearMatching(server, record -> record.ownerId() != null && ownerIds.contains(record.ownerId()));
}
public static int clearEverything(MinecraftServer server) {
return clearMatching(server, record -> true);
}
private static int clearMatching(MinecraftServer server, java.util.function.Predicate<PairRecord> filter) {
PortalGunSavedData data = get(server);
Set<UUID> portalIds = new HashSet<>();
boolean removed = data.pairs.values().removeIf(record -> {
if (!filter.test(record)) {
return false;
}
addPortalIds(portalIds, record);
return true;
});
if (removed) {
data.setDirty();
}
for (UUID portalId : portalIds) {
discardIfLoaded(server, portalId);
}
return portalIds.size();
}
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()) {
@@ -196,6 +229,6 @@ public final class PortalGunSavedData extends SavedData {
private record PairKey(ResourceLocation dimension, UUID gunId) {
}
private record PairRecord(UUID bluePortalId, UUID orangePortalId) {
private record PairRecord(UUID bluePortalId, UUID orangePortalId, UUID ownerId) {
}
}
@@ -6,7 +6,8 @@ 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");
P_BODY("p_body", 0xF9D660, 0xC0282A, "yellow", "red"),
CRAISIN("craisin", 0x6A2BE0, 0xF9D660, "purple", "gold");
private final String key;
private final int blueColor;
@@ -0,0 +1,54 @@
package com.craisinlord.antarchy.content.recipe;
import com.craisinlord.antarchy.content.item.PortalGunItem;
import com.mojang.serialization.MapCodec;
import net.minecraft.core.HolderLookup;
import net.minecraft.network.RegistryFriendlyByteBuf;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.crafting.CraftingInput;
import net.minecraft.world.item.crafting.RecipeSerializer;
import net.minecraft.world.item.crafting.ShapelessRecipe;
public class PortalGunVariantRecipe extends ShapelessRecipe {
public static final RecipeSerializer<PortalGunVariantRecipe> SERIALIZER = new Serializer();
public PortalGunVariantRecipe(ShapelessRecipe recipe) {
super(recipe.getGroup(), recipe.category(), recipe.getResultItem(null), recipe.getIngredients());
}
@Override
public ItemStack assemble(CraftingInput input, HolderLookup.Provider registries) {
ItemStack result = super.assemble(input, registries);
for (int slot = 0; slot < input.size(); slot++) {
ItemStack ingredient = input.getItem(slot);
if (ingredient.getItem() instanceof PortalGunItem) {
result.applyComponents(ingredient.getComponentsPatch());
break;
}
}
return result;
}
@Override
public RecipeSerializer<?> getSerializer() {
return SERIALIZER;
}
private static final class Serializer implements RecipeSerializer<PortalGunVariantRecipe> {
private static final MapCodec<PortalGunVariantRecipe> CODEC =
RecipeSerializer.SHAPELESS_RECIPE.codec().xmap(PortalGunVariantRecipe::new, recipe -> recipe);
private static final StreamCodec<RegistryFriendlyByteBuf, PortalGunVariantRecipe> STREAM_CODEC =
RecipeSerializer.SHAPELESS_RECIPE.streamCodec().map(PortalGunVariantRecipe::new, recipe -> recipe);
@Override
public MapCodec<PortalGunVariantRecipe> codec() {
return CODEC;
}
@Override
public StreamCodec<RegistryFriendlyByteBuf, PortalGunVariantRecipe> streamCodec() {
return STREAM_CODEC;
}
}
}
@@ -24,7 +24,7 @@ public final class BedBugSurfaceClusterFeature extends Feature<NoneFeatureConfig
private static final int SURFACE_SAMPLE_ATTEMPTS = 24;
private static final int FLOOR_SCAN_RANGE = 12;
private static final int MIN_BUGS_PER_FEATURE = 1;
private static final int MAX_BUGS_PER_FEATURE = 4;
private static final int MAX_BUGS_PER_FEATURE = 3;
public BedBugSurfaceClusterFeature(Codec<NoneFeatureConfiguration> codec) {
super(codec);
@@ -0,0 +1,24 @@
package com.craisinlord.antarchy.mixins;
import com.craisinlord.antarchy.content.item.PortalGunItem;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.item.ItemEntity;
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(Entity.class)
public abstract class PortalGunItemEntityRemovalMixin {
@Inject(method = "setRemoved", at = @At("HEAD"))
private void antarchy$clearPortalsOfDestroyedGun(Entity.RemovalReason reason, CallbackInfo ci) {
if ((Object) this instanceof ItemEntity itemEntity
&& (reason == Entity.RemovalReason.KILLED || reason == Entity.RemovalReason.DISCARDED)
&& !itemEntity.isRemoved()
&& itemEntity.level() instanceof ServerLevel level
&& itemEntity.getItem().getItem() instanceof PortalGunItem) {
PortalGunItem.clearPortalsOf(level, itemEntity.getItem());
}
}
}
@@ -0,0 +1,26 @@
package com.craisinlord.antarchy.mixins;
import com.craisinlord.antarchy.content.entity.royal.RoyalBossEntity;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import java.util.stream.Stream;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.AABB;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
@Mixin(Entity.class)
public abstract class RoyalBossMoveScanMixin {
@WrapOperation(
method = "move",
at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/Level;getBlockStatesIfLoaded(Lnet/minecraft/world/phys/AABB;)Ljava/util/stream/Stream;")
)
private Stream<BlockState> antarchy$throttleRoyalFireScan(Level level, AABB bounds, Operation<Stream<BlockState>> original) {
if ((Object) this instanceof RoyalBossEntity royal && !royal.shouldRunHeavyBlockChecks()) {
return Stream.empty();
}
return original.call(level, bounds);
}
}
@@ -10,6 +10,8 @@
"PortalGunEntityInWallMixin",
"PortalGunPlayerMovePacketMixin",
"PortalGunProjectileHitMixin",
"PortalGunItemEntityRemovalMixin",
"RoyalBossMoveScanMixin",
"ServerGamePacketListenerTeleportAccessor",
"PortalGunZombieMixin",
"FallingBlockEntityAccessor",
@@ -673,6 +673,7 @@
"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.portal_gun_craisin": "Craisin Portal Gun",
"item.antarchy.diamond_minecart": "The Diamond Minecart",
"item.antarchy.cogwheel_pet": "Cogwheel Pet",
"item.antarchy.fire_dragon_pet": "Fire Dragon Pet",
@@ -841,11 +842,14 @@
"tooltip.antarchy.gravity_gun.blacklist": "Does not affect blacklisted blocks or entities.",
"tooltip.antarchy.portal_gun.primary": "Attack: fire a %s portal.",
"tooltip.antarchy.portal_gun.secondary": "Use: fire a %s portal.",
"commands.antarchy.portalgun.clear.all": "Cleared %s portal gun portals",
"commands.antarchy.portalgun.clear.owners": "Cleared %s portal gun portals owned by %s players",
"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.color.gold": "gold",
"tooltip.antarchy.portal_gun.reset": "Sneak + use: fizzle your active portals.",
"tooltip.antarchy.antimetal_inversion": "Inverts entites directly below it",
"tooltip.antarchy.diamond_minecart.forward": "Will be implemented later.",
@@ -0,0 +1,7 @@
{
"parent": "minecraft:item/generated",
"gui_light": "front",
"textures": {
"layer0": "antarchy:item/portal_gun/portal_gun_craisin_item"
}
}
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: 555 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 418 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

@@ -1,5 +1,5 @@
{
"type": "minecraft:crafting_shapeless",
"type": "antarchy:portal_gun_variant",
"category": "equipment",
"ingredients": [
{
@@ -0,0 +1,19 @@
{
"type": "antarchy:portal_gun_variant",
"category": "equipment",
"ingredients": [
{
"item": "antarchy:portal_gun"
},
{
"item": "minecraft:purple_dye"
},
{
"item": "minecraft:gold_ingot"
}
],
"result": {
"id": "antarchy:portal_gun_craisin",
"count": 1
}
}
@@ -1,5 +1,5 @@
{
"type": "minecraft:crafting_shapeless",
"type": "antarchy:portal_gun_variant",
"category": "equipment",
"ingredients": [
{