diff --git a/gradle.properties b/gradle.properties index aaf800e..01a69ca 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,7 +6,7 @@ minecraft_version=1.20.1 yarn_mappings=1.20.1+build.10 loader_version=0.18.3 # Mod Properties -mod_version=26.3.17.4 +mod_version=26.3.18 maven_group=dev.tggamesyt archives_base_name=szar # Dependencies diff --git a/src/main/java/dev/tggamesyt/szar/PortalBlock.java b/src/main/java/dev/tggamesyt/szar/PortalBlock.java index c756e2e..d000511 100644 --- a/src/main/java/dev/tggamesyt/szar/PortalBlock.java +++ b/src/main/java/dev/tggamesyt/szar/PortalBlock.java @@ -42,12 +42,12 @@ public class PortalBlock extends Block { @Override public void onEntityCollision(BlockState state, World world, BlockPos pos, Entity entity) { if (world.isClient) return; - if (!(entity instanceof ServerPlayerEntity player)) return; + // Cooldown check long now = world.getTime(); - Long last = cooldowns.get(player.getUuid()); + Long last = cooldowns.get(entity.getUuid()); if (last != null && now - last < 60) return; - cooldowns.put(player.getUuid(), now); + cooldowns.put(entity.getUuid(), now); TrackerBlockEntity tracker = findTrackerAbove(world, pos); if (tracker == null) return; @@ -55,11 +55,51 @@ public class PortalBlock extends Block { MinecraftServer server = world.getServer(); if (server == null) return; - // Detect dimension instead of reading isNetherSide - if (world.getRegistryKey() == World.OVERWORLD) { - teleportToNether(player, tracker, server, pos); - } else if (world.getRegistryKey() == Szar.BACKROOMS_KEY) { - teleportToOverworld(player, tracker, server); + if (entity instanceof ServerPlayerEntity player) { + // Full player handling — inventory save, tracker registration, etc. + if (world.getRegistryKey() == World.OVERWORLD) { + teleportToNether(player, tracker, server, pos); + } else if (world.getRegistryKey() == Szar.BACKROOMS_KEY) { + teleportToOverworld(player, tracker, server); + } + } else { + // Non-player entity — just teleport, no inventory or tracker registration + if (world.getRegistryKey() == World.OVERWORLD) { + ServerWorld backrooms = server.getWorld(Szar.BACKROOMS_KEY); + if (backrooms == null) return; + double safeY = findSafeY(backrooms, (int) entity.getX(), (int) entity.getZ()); + entity.teleport(backrooms, entity.getX(), safeY, entity.getZ(), + java.util.Set.of(), entity.getYaw(), entity.getPitch()); + } else { + // Non-player entity — just teleport, no inventory or tracker registration + if (world.getRegistryKey() == World.OVERWORLD) { + ServerWorld backrooms = server.getWorld(Szar.BACKROOMS_KEY); + if (backrooms == null) return; + double safeY = findSafeY(backrooms, (int) entity.getX(), (int) entity.getZ()); + entity.teleport(backrooms, entity.getX(), safeY, entity.getZ(), + java.util.Set.of(), entity.getYaw(), entity.getPitch()); + } else if (world.getRegistryKey() == Szar.BACKROOMS_KEY) { + ServerWorld overworld = server.getWorld(World.OVERWORLD); + if (overworld == null) return; + + double baseX = tracker.returnX; + double baseY = tracker.returnY; + double baseZ = tracker.returnZ; + + // If no player has used this portal yet, fallback + if (baseX == 0 && baseY == 0 && baseZ == 0) { + double safeY = findSafeY(overworld, (int) entity.getX(), (int) entity.getZ()); + entity.teleport(overworld, entity.getX(), safeY, entity.getZ(), + java.util.Set.of(), entity.getYaw(), entity.getPitch()); + return; + } + + // Search up to 10 blocks offset in XZ and Y for a safe spot not above a portal + BlockPos safePos = findSafeSpotNearEntry(overworld, baseX, baseY, baseZ); + entity.teleport(overworld, safePos.getX() + 0.5, safePos.getY(), safePos.getZ() + 0.5, + java.util.Set.of(), entity.getYaw(), entity.getPitch()); + } + } } } @@ -74,6 +114,11 @@ public class PortalBlock extends Block { tag.putInt("OwnerTrackerY", tracker.getPos().getY()); tag.putInt("OwnerTrackerZ", tracker.getPos().getZ()); + tracker.returnX = player.getX(); + tracker.returnY = player.getY() + 6; + tracker.returnZ = player.getZ(); + tracker.markDirty(); + // Save inventory NbtList savedInventory = saveInventory(player); saveInventoryToPlayer(player, savedInventory); @@ -94,6 +139,20 @@ public class PortalBlock extends Block { player.teleport(nether, netherX, netherY, netherZ, player.getYaw(), player.getPitch()); + + BlockPos destPortalPos = new BlockPos((int) netherX, (int) netherY, (int) netherZ); + for (int i = 1; i <= 5; i++) { + BlockPos check = destPortalPos.up(i); + if (nether.getBlockState(check).getBlock() instanceof TrackerBlock) { + if (nether.getBlockEntity(check) instanceof TrackerBlockEntity backroomsTracker) { + backroomsTracker.returnX = player.getX(); + backroomsTracker.returnY = player.getY() + 6; + backroomsTracker.returnZ = player.getZ(); + backroomsTracker.markDirty(); + } + break; + } + } } private void teleportToOverworld(ServerPlayerEntity player, TrackerBlockEntity netherTracker, @@ -103,7 +162,6 @@ public class PortalBlock extends Block { double returnY = tag.getDouble("EntryY"); double returnZ = tag.getDouble("EntryZ"); - // Read overworld tracker pos BlockPos owTrackerPos = null; if (tag.contains("OwnerTrackerX")) { owTrackerPos = new BlockPos( @@ -117,24 +175,38 @@ public class PortalBlock extends Block { netherTracker.removePlayer(player.getUuid()); ServerWorld overworld = server.getWorld(World.OVERWORLD); - ServerWorld nether = server.getWorld(Szar.BACKROOMS_KEY); + ServerWorld backrooms = server.getWorld(Szar.BACKROOMS_KEY); - // Clean up nether side if empty - if (!netherTracker.hasPlayers() && nether != null) { - TrackerBlock.restoreAndCleanup(nether, - netherTracker.getPos(), netherTracker, server); - } - - // Clean up overworld tracker too if (owTrackerPos != null && overworld != null) { if (overworld.getBlockEntity(owTrackerPos) instanceof TrackerBlockEntity owTracker) { - owTracker.removePlayer(player.getUuid()); - if (!owTracker.hasPlayers()) { - TrackerBlock.restoreAndCleanup(overworld, owTrackerPos, owTracker, server); + // Resolve to root of the overworld group + TrackerBlockEntity root = owTracker.getRoot(overworld); + root.removePlayer(player.getUuid()); + + if (!root.hasPlayers()) { + // Collect and clean up all connected trackers + List allTrackers = new ArrayList<>(); + allTrackers.add(root.getPos()); + for (BlockPos childPortal : root.getControlledPortals()) { + allTrackers.add(childPortal.up(4)); + } + + for (BlockPos trackerPos : allTrackers) { + if (overworld.getBlockEntity(trackerPos) + instanceof TrackerBlockEntity te) { + TrackerBlock.restoreAndCleanup(overworld, trackerPos, te, server); + } + } } } } + // Clean up backrooms tracker too + if (!netherTracker.hasPlayers() && backrooms != null) { + TrackerBlock.restoreAndCleanup(backrooms, + netherTracker.getPos(), netherTracker, server); + } + if (overworld == null) return; player.teleport(overworld, returnX, returnY, returnZ, player.getYaw(), player.getPitch()); @@ -147,7 +219,8 @@ public class PortalBlock extends Block { BlockPos check = portalPos.up(i); if (world.getBlockState(check).getBlock() instanceof TrackerBlock) { if (world.getBlockEntity(check) instanceof TrackerBlockEntity te) { - return te; + // Always delegate to root + return te.getRoot(world); } } } @@ -263,4 +336,37 @@ public class PortalBlock extends Block { state.removePlayerData(player.getUuid()); } + + private BlockPos findSafeSpotNearEntry(ServerWorld world, double baseX, double baseY, double baseZ) { + System.out.println(baseX + ", " + baseY + ", " + baseZ); + int bx = (int) baseX; + int by = (int) baseY; + int bz = (int) baseZ; + + // First try exact position + BlockPos exact = new BlockPos(bx, by, bz); + if (isSafeAndNotPortal(world, exact)) return exact; + + // Spiral outward in XZ only, keep Y fixed to entry Y + for (int radius = 1; radius <= 10; radius++) { + for (int dx = -radius; dx <= radius; dx++) { + for (int dz = -radius; dz <= radius; dz++) { + if (Math.abs(dx) != radius && Math.abs(dz) != radius) continue; + BlockPos candidate = new BlockPos(bx + dx, by, bz + dz); + if (isSafeAndNotPortal(world, candidate)) return candidate; + } + } + } + + return exact; // fallback + } + + private boolean isSafeAndNotPortal(ServerWorld world, BlockPos feet) { + BlockPos head = feet.up(); + BlockPos ground = feet.down(); + return !world.getBlockState(feet).isSolidBlock(world, feet) + && !world.getBlockState(head).isSolidBlock(world, head) + && world.getBlockState(ground).isSolidBlock(world, ground) + && !(world.getBlockState(ground).getBlock() instanceof PortalBlock); + } } \ No newline at end of file diff --git a/src/main/java/dev/tggamesyt/szar/Szar.java b/src/main/java/dev/tggamesyt/szar/Szar.java index 2762f4b..a5a5e21 100644 --- a/src/main/java/dev/tggamesyt/szar/Szar.java +++ b/src/main/java/dev/tggamesyt/szar/Szar.java @@ -355,8 +355,6 @@ public class Szar implements ModInitializer { // random ahh silly stuff entries.add(Szar.POPTART); entries.add(Szar.NYAN_SPAWNEGG); - entries.add(Szar.EPSTEIN_FILES); - entries.add(Szar.EPSTEIN_SPAWNEGG); entries.add(Szar.BAITER_DISC); entries.add(Szar.MERL_SPAWNEGG); entries.add(Szar.EFN_DISK); @@ -416,6 +414,8 @@ public class Szar implements ModInitializer { entries.add(Szar.NIGGERITE_BOOTS); entries.add(Szar.NIGGERITE_BLOCK); // nsfw + entries.add(Szar.EPSTEIN_FILES); + entries.add(Szar.EPSTEIN_SPAWNEGG); entries.add(Szar.FASZITEM); entries.add(Szar.CNDM); entries.add(Szar.LATEX); diff --git a/src/main/java/dev/tggamesyt/szar/TrackerBlock.java b/src/main/java/dev/tggamesyt/szar/TrackerBlock.java index d760bb2..d7931e0 100644 --- a/src/main/java/dev/tggamesyt/szar/TrackerBlock.java +++ b/src/main/java/dev/tggamesyt/szar/TrackerBlock.java @@ -10,15 +10,14 @@ import net.minecraft.server.MinecraftServer; import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.server.world.ServerWorld; import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Direction; import net.minecraft.util.shape.VoxelShape; import net.minecraft.util.shape.VoxelShapes; import net.minecraft.world.BlockView; import net.minecraft.world.World; import org.jetbrains.annotations.Nullable; -import java.util.HashSet; -import java.util.Set; -import java.util.UUID; +import java.util.*; public class TrackerBlock extends Block implements BlockEntityProvider { @@ -71,24 +70,67 @@ public class TrackerBlock extends Block implements BlockEntityProvider { @Nullable LivingEntity placer, ItemStack itemStack) { if (world.isClient) return; if (!(world.getBlockEntity(pos) instanceof TrackerBlockEntity tracker)) return; - - // Only auto-place portal if a player placed this tracker if (!tracker.placedByPlayer) return; + // Check all 6 neighbors for an existing tracker + TrackerBlockEntity parentTracker = findNeighborTracker(world, pos); + BlockPos portalPos = pos.down(4); + BlockState originalBlock = world.getBlockState(portalPos); - // Save the original block before replacing it - tracker.originalPortalBlock = world.getBlockState(portalPos); - tracker.markDirty(); + if (parentTracker != null) { + // Merge — this becomes a child + tracker.parentTrackerPos = parentTracker.getPos(); + tracker.originalPortalBlock = originalBlock; + tracker.markDirty(); - world.setBlockState(portalPos, Szar.PORTAL_BLOCK.getDefaultState()); + // Register portal with parent + parentTracker.addPortal(portalPos); + + // Place portal block + world.setBlockState(portalPos, Szar.PORTAL_BLOCK.getDefaultState()); + } else { + // Standalone — normal behavior + tracker.originalPortalBlock = originalBlock; + tracker.markDirty(); + world.setBlockState(portalPos, Szar.PORTAL_BLOCK.getDefaultState()); + } + } + + @Nullable + private TrackerBlockEntity findNeighborTracker(World world, BlockPos pos) { + for (Direction dir : Direction.values()) { + BlockPos neighbor = pos.offset(dir); + if (world.getBlockState(neighbor).getBlock() instanceof TrackerBlock) { + if (world.getBlockEntity(neighbor) instanceof TrackerBlockEntity te) { + // Always return the root of the neighbor + return te.getRoot(world); + } + } + } + return null; } @Override public void onBreak(World world, BlockPos pos, BlockState state, PlayerEntity player) { - // In survival, only break if holding the special item - if (!world.isClient && world.getBlockEntity(pos) instanceof TrackerBlockEntity tracker) { - restoreAndCleanup(world, pos, tracker, world.getServer()); + // Resolve to root + TrackerBlockEntity root = tracker.getRoot(world); + BlockPos rootPos = root.getPos(); + + // Collect all connected tracker positions (root + all children) + List allTrackers = new ArrayList<>(); + allTrackers.add(rootPos); + for (BlockPos childPortal : root.getControlledPortals()) { + allTrackers.add(childPortal.up(4)); + } + + // Clean up each tracker and its portal + for (BlockPos trackerPos : allTrackers) { + if (world.getBlockEntity(trackerPos) instanceof TrackerBlockEntity te) { + cleanupPortalOnly(world, trackerPos, te, world.getServer()); + world.removeBlock(trackerPos, false); + } + } } super.onBreak(world, pos, state, player); } @@ -97,32 +139,60 @@ public class TrackerBlock extends Block implements BlockEntityProvider { TrackerBlockEntity tracker, MinecraftServer server) { if (server == null) return; - // Kick all players tracked here back to overworld Set players = new HashSet<>(tracker.getPlayersInside()); for (UUID uuid : players) { ServerPlayerEntity player = server.getPlayerManager().getPlayer(uuid); if (player == null) continue; + ServerWorld overworld = server.getWorld(World.OVERWORLD); + if (overworld == null) continue; + + // Read coords BEFORE restoreInventory wipes the state + NbtCompound tag = PortalDataState.getOrCreate(overworld) + .getOrCreatePlayerData(uuid); + double rx = tag.getDouble("EntryX"); + double ry = tag.getDouble("EntryY"); + double rz = tag.getDouble("EntryZ"); + PortalBlock.restoreInventory(player, server); - ServerWorld overworld = server.getWorld(World.OVERWORLD); - if (overworld != null) { - NbtCompound tag = PortalDataState.getOrCreate(overworld) - .getOrCreatePlayerData(uuid); - double rx = tag.getDouble("EntryX"); - double ry = tag.getDouble("EntryY"); - double rz = tag.getDouble("EntryZ"); - player.teleport(overworld, rx, ry, rz, player.getYaw(), player.getPitch()); - } + player.teleport(overworld, rx, ry, rz, player.getYaw(), player.getPitch()); } - // Restore original block at portal position BlockPos portalPos = trackerPos.down(4); if (world.getBlockState(portalPos).getBlock() instanceof PortalBlock) { world.setBlockState(portalPos, tracker.originalPortalBlock); } - // Remove the tracker itself world.removeBlock(trackerPos, false); } + public static void cleanupPortalOnly(World world, BlockPos trackerPos, + TrackerBlockEntity tracker, MinecraftServer server) { + if (server == null) return; + + Set players = new HashSet<>(tracker.getPlayersInside()); + for (UUID uuid : players) { + ServerPlayerEntity player = server.getPlayerManager().getPlayer(uuid); + if (player == null) continue; + + ServerWorld overworld = server.getWorld(World.OVERWORLD); + if (overworld == null) continue; + + // Read coords BEFORE restoreInventory wipes the state + NbtCompound tag = PortalDataState.getOrCreate(overworld) + .getOrCreatePlayerData(uuid); + double rx = tag.getDouble("EntryX"); + double ry = tag.getDouble("EntryY"); + double rz = tag.getDouble("EntryZ"); + + PortalBlock.restoreInventory(player, server); + + player.teleport(overworld, rx, ry, rz, player.getYaw(), player.getPitch()); + } + + BlockPos portalPos = trackerPos.down(4); + if (world.getBlockState(portalPos).getBlock() instanceof PortalBlock) { + world.setBlockState(portalPos, tracker.originalPortalBlock); + } + } } \ No newline at end of file diff --git a/src/main/java/dev/tggamesyt/szar/TrackerBlockEntity.java b/src/main/java/dev/tggamesyt/szar/TrackerBlockEntity.java index 20e225e..08c6d67 100644 --- a/src/main/java/dev/tggamesyt/szar/TrackerBlockEntity.java +++ b/src/main/java/dev/tggamesyt/szar/TrackerBlockEntity.java @@ -8,10 +8,10 @@ import net.minecraft.nbt.NbtList; import net.minecraft.nbt.NbtOps; import net.minecraft.nbt.NbtString; import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +import org.jetbrains.annotations.Nullable; -import java.util.HashSet; -import java.util.Set; -import java.util.UUID; +import java.util.*; public class TrackerBlockEntity extends BlockEntity { // Add these fields @@ -72,6 +72,21 @@ public class TrackerBlockEntity extends BlockEntity { .ifPresent(nbt1 -> originalBlock.put("State", nbt1)); nbt.put("OriginalPortalBlock", originalBlock); nbt.putBoolean("PlacedByPlayer", placedByPlayer); + if (parentTrackerPos != null) { + nbt.putInt("ParentX", parentTrackerPos.getX()); + nbt.putInt("ParentY", parentTrackerPos.getY()); + nbt.putInt("ParentZ", parentTrackerPos.getZ()); + } + + NbtList portalList = new NbtList(); + for (BlockPos p : controlledPortals) { + NbtCompound entry = new NbtCompound(); + entry.putInt("X", p.getX()); + entry.putInt("Y", p.getY()); + entry.putInt("Z", p.getZ()); + portalList.add(entry); + } + nbt.put("ControlledPortals", portalList); } @Override @@ -103,5 +118,63 @@ public class TrackerBlockEntity extends BlockEntity { } } placedByPlayer = nbt.getBoolean("PlacedByPlayer"); + if (nbt.contains("ParentX")) { + parentTrackerPos = new BlockPos( + nbt.getInt("ParentX"), + nbt.getInt("ParentY"), + nbt.getInt("ParentZ") + ); + } else { + parentTrackerPos = null; + } + + controlledPortals.clear(); + NbtList portalList = nbt.getList("ControlledPortals", 10); + for (int i = 0; i < portalList.size(); i++) { + NbtCompound entry = portalList.getCompound(i); + controlledPortals.add(new BlockPos( + entry.getInt("X"), entry.getInt("Y"), entry.getInt("Z") + )); + } + } + + // null = this is a root/parent tracker +// non-null = this is a child, delegate to parent + @Nullable + public BlockPos parentTrackerPos = null; + + // List of portal positions this tracker controls (only used by parent) + private final List controlledPortals = new ArrayList<>(); + + public void addPortal(BlockPos portalPos) { + if (!controlledPortals.contains(portalPos)) { + controlledPortals.add(portalPos); + markDirty(); + } + } + + public void removePortal(BlockPos portalPos) { + controlledPortals.remove(portalPos); + markDirty(); + } + + public List getControlledPortals() { + return controlledPortals; + } + + public boolean isChild() { + return parentTrackerPos != null; + } + + // Resolves to the root parent, following chain if needed + public TrackerBlockEntity getRoot(World world) { + if (parentTrackerPos == null) return this; + if (world.getBlockEntity(parentTrackerPos) instanceof TrackerBlockEntity parent) { + return parent.getRoot(world); + } + // Parent is gone — become root + parentTrackerPos = null; + markDirty(); + return this; } } \ No newline at end of file diff --git a/src/main/java/dev/tggamesyt/szar/mixin/NoClipMixin.java b/src/main/java/dev/tggamesyt/szar/mixin/NoClipMixin.java index 94420fb..2d4ca77 100644 --- a/src/main/java/dev/tggamesyt/szar/mixin/NoClipMixin.java +++ b/src/main/java/dev/tggamesyt/szar/mixin/NoClipMixin.java @@ -27,9 +27,6 @@ public class NoClipMixin { private void szar_noClipBelowTracker(BlockView world, BlockPos pos, ShapeContext ctx, CallbackInfoReturnable cir) { - if (!(ctx instanceof EntityShapeContext esc)) return; - if (!(esc.getEntity() instanceof PlayerEntity)) return; - for (int i = 1; i <= 5; i++) { BlockPos above = pos.up(i); if (world.getBlockState(above).getBlock() instanceof TrackerBlock) { diff --git a/src/main/resources/assets/szar/textures/block/ceiling_tile_color.png b/src/main/resources/assets/szar/textures/block/ceiling_tile_color.png index ca63937..ec4b254 100644 Binary files a/src/main/resources/assets/szar/textures/block/ceiling_tile_color.png and b/src/main/resources/assets/szar/textures/block/ceiling_tile_color.png differ diff --git a/src/main/resources/assets/szar/textures/block/ceiling_tile_glitched_color.png b/src/main/resources/assets/szar/textures/block/ceiling_tile_glitched_color.png index 396270b..18af7a3 100644 Binary files a/src/main/resources/assets/szar/textures/block/ceiling_tile_glitched_color.png and b/src/main/resources/assets/szar/textures/block/ceiling_tile_glitched_color.png differ diff --git a/src/main/resources/assets/szar/textures/block/old_backrooms_textures/ceiling_tile_color.png b/src/main/resources/assets/szar/textures/block/old_backrooms_textures/ceiling_tile_color.png new file mode 100644 index 0000000..ca63937 Binary files /dev/null and b/src/main/resources/assets/szar/textures/block/old_backrooms_textures/ceiling_tile_color.png differ diff --git a/src/main/resources/assets/szar/textures/block/old_backrooms_textures/ceiling_tile_glitched_color.png b/src/main/resources/assets/szar/textures/block/old_backrooms_textures/ceiling_tile_glitched_color.png new file mode 100644 index 0000000..396270b Binary files /dev/null and b/src/main/resources/assets/szar/textures/block/old_backrooms_textures/ceiling_tile_glitched_color.png differ diff --git a/src/main/resources/assets/szar/textures/block/old_backrooms_textures/plastic.png b/src/main/resources/assets/szar/textures/block/old_backrooms_textures/plastic.png new file mode 100644 index 0000000..39904d4 Binary files /dev/null and b/src/main/resources/assets/szar/textures/block/old_backrooms_textures/plastic.png differ diff --git a/src/main/resources/assets/szar/textures/block/old_backrooms_textures/wallpaper_bottom_block_texture.png b/src/main/resources/assets/szar/textures/block/old_backrooms_textures/wallpaper_bottom_block_texture.png new file mode 100644 index 0000000..b2609db Binary files /dev/null and b/src/main/resources/assets/szar/textures/block/old_backrooms_textures/wallpaper_bottom_block_texture.png differ diff --git a/src/main/resources/assets/szar/textures/block/plastic.png b/src/main/resources/assets/szar/textures/block/plastic.png index 39904d4..da4702e 100644 Binary files a/src/main/resources/assets/szar/textures/block/plastic.png and b/src/main/resources/assets/szar/textures/block/plastic.png differ diff --git a/src/main/resources/assets/szar/textures/block/wallpaper_bottom_block_texture.png b/src/main/resources/assets/szar/textures/block/wallpaper_bottom_block_texture.png index b2609db..47d4171 100644 Binary files a/src/main/resources/assets/szar/textures/block/wallpaper_bottom_block_texture.png and b/src/main/resources/assets/szar/textures/block/wallpaper_bottom_block_texture.png differ