This commit is contained in:
2026-03-18 18:29:31 +01:00
parent 12ad7d6f04
commit 4f455ead5f
4 changed files with 250 additions and 37 deletions

View File

@@ -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.18.2
mod_version=26.3.18.3
maven_group=dev.tggamesyt
archives_base_name=szar
# Dependencies

View File

@@ -0,0 +1,215 @@
package dev.tggamesyt.szar;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents;
import net.minecraft.block.entity.BarrelBlockEntity;
import net.minecraft.entity.player.HungerManager;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
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.Box;
import java.util.*;
public class BackroomsBarrelManager {
private static final Map<UUID, Double> lastX = new HashMap<>();
private static final Map<UUID, Double> lastZ = new HashMap<>();
private static final Map<UUID, Double> walkAccumulator = new HashMap<>();
private static final Map<UUID, Integer> walkThreshold = new HashMap<>();
private static final Map<UUID, Set<BlockPos>> trackerBarrels = new HashMap<>();
private static final Map<UUID, Set<BlockPos>> foodBarrels = new HashMap<>();
public static void register() {
ServerTickEvents.END_SERVER_TICK.register(BackroomsBarrelManager::tick);
}
private static void tick(MinecraftServer server) {
ServerWorld backrooms = server.getWorld(Szar.BACKROOMS_KEY);
if (backrooms == null) return;
for (ServerPlayerEntity player : backrooms.getPlayers()) {
UUID uuid = player.getUuid();
// --- Walk tracking ---
double px = player.getX();
double pz = player.getZ();
if (lastX.containsKey(uuid)) {
double dx = px - lastX.get(uuid);
double dz = pz - lastZ.get(uuid);
double dist = Math.sqrt(dx * dx + dz * dz);
walkAccumulator.merge(uuid, dist, Double::sum);
}
lastX.put(uuid, px);
lastZ.put(uuid, pz);
if (!walkThreshold.containsKey(uuid)) {
walkThreshold.put(uuid, randomThreshold(backrooms.random));
}
// --- Check if tracker barrels need clearing ---
if (trackerBarrels.containsKey(uuid)) {
checkAndClearTrackerBarrels(backrooms, uuid);
}
// --- Check if food barrels need clearing ---
if (foodBarrels.containsKey(uuid)) {
checkAndClearFoodBarrels(backrooms, uuid);
}
// --- Hunger check (every 20 ticks) ---
if (backrooms.getTime() % 20 == 0) {
HungerManager hunger = player.getHungerManager();
if (hunger.getFoodLevel() <= 10 && !hasAnyFood(player)) {
if (!foodBarrels.containsKey(uuid)) {
List<BarrelBlockEntity> nearby = getNearbyBarrels(backrooms, player, 16);
if (!nearby.isEmpty()) {
Set<BlockPos> positions = new HashSet<>();
for (BarrelBlockEntity barrel : nearby) {
placeItemInBarrel(barrel, new ItemStack(Szar.CAN_OF_BEANS));
positions.add(barrel.getPos().toImmutable());
}
foodBarrels.put(uuid, positions);
}
}
}
}
// --- Walk threshold check ---
double walked = walkAccumulator.getOrDefault(uuid, 0.0);
if (walked >= walkThreshold.getOrDefault(uuid, 500)) {
List<BarrelBlockEntity> nearby = getNearbyBarrels(backrooms, player, 16);
if (!nearby.isEmpty()) {
Set<BlockPos> positions = new HashSet<>();
for (BarrelBlockEntity barrel : nearby) {
placeItemInBarrel(barrel, new ItemStack(Szar.TRACKER_BLOCK_ITEM));
positions.add(barrel.getPos().toImmutable());
}
trackerBarrels.put(uuid, positions);
}
walkAccumulator.put(uuid, 0.0);
walkThreshold.put(uuid, randomThreshold(backrooms.random));
}
}
// Clean up data for players no longer in backrooms
Set<UUID> activePlayers = new HashSet<>();
for (ServerPlayerEntity p : backrooms.getPlayers()) activePlayers.add(p.getUuid());
lastX.keySet().retainAll(activePlayers);
lastZ.keySet().retainAll(activePlayers);
walkAccumulator.keySet().retainAll(activePlayers);
walkThreshold.keySet().retainAll(activePlayers);
foodBarrels.keySet().retainAll(activePlayers);
trackerBarrels.keySet().retainAll(activePlayers);
}
private static void checkAndClearTrackerBarrels(ServerWorld world, UUID uuid) {
Set<BlockPos> positions = trackerBarrels.get(uuid);
if (positions == null) return;
boolean anyTaken = false;
for (BlockPos pos : positions) {
if (world.getBlockEntity(pos) instanceof BarrelBlockEntity barrel) {
if (!barrelHasItem(barrel, Szar.TRACKER_BLOCK_ITEM.asItem())) {
anyTaken = true;
break;
}
}
}
if (anyTaken) {
for (BlockPos pos : positions) {
if (world.getBlockEntity(pos) instanceof BarrelBlockEntity barrel) {
removeItemFromBarrel(barrel, Szar.TRACKER_BLOCK_ITEM.asItem());
}
}
trackerBarrels.remove(uuid);
}
}
private static void checkAndClearFoodBarrels(ServerWorld world, UUID uuid) {
Set<BlockPos> positions = foodBarrels.get(uuid);
if (positions == null) return;
boolean anyTaken = false;
for (BlockPos pos : positions) {
if (world.getBlockEntity(pos) instanceof BarrelBlockEntity barrel) {
if (!barrelHasItem(barrel, Szar.CAN_OF_BEANS.asItem())) {
anyTaken = true;
break;
}
}
}
if (anyTaken) {
for (BlockPos pos : positions) {
if (world.getBlockEntity(pos) instanceof BarrelBlockEntity barrel) {
removeItemFromBarrel(barrel, Szar.CAN_OF_BEANS.asItem());
}
}
foodBarrels.remove(uuid);
}
}
private static boolean barrelHasItem(BarrelBlockEntity barrel, Item item) {
for (int i = 0; i < barrel.size(); i++) {
if (barrel.getStack(i).isOf(item)) return true;
}
return false;
}
private static void removeItemFromBarrel(BarrelBlockEntity barrel, Item item) {
for (int i = 0; i < barrel.size(); i++) {
if (barrel.getStack(i).isOf(item)) {
barrel.setStack(i, ItemStack.EMPTY);
barrel.markDirty();
return;
}
}
}
private static boolean hasAnyFood(ServerPlayerEntity player) {
for (int i = 0; i < player.getInventory().size(); i++) {
ItemStack stack = player.getInventory().getStack(i);
if (!stack.isEmpty() && (stack.isFood()
|| stack.isOf(Szar.CAN_OF_BEANS))) {
return true;
}
}
return false;
}
private static void placeItemInBarrel(BarrelBlockEntity barrel, ItemStack item) {
for (int i = 0; i < barrel.size(); i++) {
if (barrel.getStack(i).isEmpty()) {
barrel.setStack(i, item.copy());
barrel.markDirty();
return;
}
}
}
private static List<BarrelBlockEntity> getNearbyBarrels(ServerWorld world,
ServerPlayerEntity player,
int radius) {
List<BarrelBlockEntity> result = new ArrayList<>();
Box box = player.getBoundingBox().expand(radius);
BlockPos min = BlockPos.ofFloored(box.minX, box.minY, box.minZ);
BlockPos max = BlockPos.ofFloored(box.maxX, box.maxY, box.maxZ);
for (BlockPos pos : BlockPos.iterate(min, max)) {
if (world.getBlockEntity(pos) instanceof BarrelBlockEntity barrel) {
result.add(barrel);
}
}
return result;
}
private static int randomThreshold(net.minecraft.util.math.random.Random random) {
return 500 + random.nextInt(501);
}
}

View File

@@ -101,11 +101,18 @@ public class BackroomsChunkGenerator extends ChunkGenerator {
}
if (isOpen) {
// Air inside the room
for (int y = WALL_BASE_Y; y <= WALL_TOP_Y; y++) {
chunk.setBlockState(new BlockPos(lx, y, lz),
Blocks.AIR.getDefaultState(), false);
}
// 1 in 40 chance of a barrel on the floor
// With this — 1 per ~40x40 block area:
long barrelHash = hash(worldX * 31 + 17, worldZ * 29 + 11);
if ((barrelHash % 1600) == 0) {
chunk.setBlockState(new BlockPos(lx, FLOOR_Y + 1, lz),
Blocks.BARREL.getDefaultState(), false);
}
} else {
// Wall column
chunk.setBlockState(new BlockPos(lx, WALL_BASE_Y, lz),
@@ -115,8 +122,6 @@ public class BackroomsChunkGenerator extends ChunkGenerator {
Szar.WALL_BLOCK.getDefaultState(), false);
}
}
// After the lx/lz loop, still inside populateNoise:
placeBackroomsPortals(chunk, chunkX, chunkZ);
}
}
@@ -240,55 +245,47 @@ public class BackroomsChunkGenerator extends ChunkGenerator {
@Override
public void generateFeatures(StructureWorldAccess world, Chunk chunk,
StructureAccessor structureAccessor) {
// Initialize wall block entities after chunk is placed
BlockPos.Mutable mutable = new BlockPos.Mutable();
int chunkX = chunk.getPos().getStartX();
int chunkZ = chunk.getPos().getStartZ();
// Initialize wall block entities
for (int lx = 0; lx < 16; lx++) {
for (int lz = 0; lz < 16; lz++) {
for (int y = 0; y < 64; y++) {
mutable.set(chunkX + lx, y, chunkZ + lz);
if (world.getBlockState(mutable).getBlock() instanceof WallBlock) {
if (world.getBlockEntity(mutable) == null) {
// Force block entity creation by re-setting the block
world.setBlockState(mutable, Szar.WALL_BLOCK.getDefaultState(),
Block.NOTIFY_LISTENERS);
}
world.setBlockState(mutable, Szar.WALL_BLOCK.getDefaultState(),
Block.NOTIFY_ALL);
if (world.getBlockEntity(mutable) instanceof WallBlockEntity wall) {
wall.initializeIfNeeded();
}
}
}
mutable.set(chunkX + lx, CEILING_Y - 1, chunkZ + lz); // Y=8
if (world.getBlockState(mutable).getBlock() instanceof TrackerBlock) {
if (world.getBlockEntity(mutable) == null) {
world.setBlockState(mutable, Szar.TRACKER_BLOCK.getDefaultState(),
Block.NOTIFY_ALL);
}
if (world.getBlockEntity(mutable) instanceof TrackerBlockEntity te) {
te.placedByPlayer = false;
te.originalPortalBlock = Szar.PLASTIC.getDefaultState();
te.markDirty();
}
}
}
// Place tracker portals
long chunkHash = hash((chunkX / 16) * 7 + 3, (chunkZ / 16) * 13 + 7);
if ((chunkHash % 20) == 0) {
int lx = 4 + (int)(chunkHash >> 8 & 0x7);
int lz = 4 + (int)(chunkHash >> 12 & 0x7);
if (isOpenSpace(chunkX + lx, chunkZ + lz)) {
BlockPos trackerPos = new BlockPos(chunkX + lx, FLOOR_Y + 1, chunkZ + lz);
BlockPos portalPos = trackerPos.down(4);
world.setBlockState(portalPos, Szar.PORTAL_BLOCK.getDefaultState(),
Block.NOTIFY_ALL);
world.setBlockState(trackerPos, Szar.TRACKER_BLOCK.getDefaultState(),
Block.NOTIFY_ALL);
if (world.getBlockEntity(trackerPos) instanceof TrackerBlockEntity te) {
te.placedByPlayer = false;
te.originalPortalBlock = Szar.PLASTIC.getDefaultState();
te.markDirty();
}
}
}
}
private void placeBackroomsPortals(Chunk chunk, int chunkX, int chunkZ) {
long chunkHash = hash(chunkX * 7 + 3, chunkZ * 13 + 7);
if ((chunkHash % 20) != 0) return;
int lx = 4 + (int)(chunkHash >> 8 & 0x7);
int lz = 4 + (int)(chunkHash >> 12 & 0x7);
if (!isOpenSpace(chunkX * 16 + lx, chunkZ * 16 + lz)) return;
BlockPos trackerPos = new BlockPos(lx, FLOOR_Y + 1, lz); // Y=5
BlockPos portalPos = trackerPos.down(4); // Y=1, underground
chunk.setBlockState(portalPos, Szar.PORTAL_BLOCK.getDefaultState(), false);
chunk.setBlockState(trackerPos, Szar.TRACKER_BLOCK.getDefaultState(), false);
}
}

View File

@@ -1087,6 +1087,7 @@ public class Szar implements ModInitializer {
RegistryKey.of(RegistryKeys.PLACED_FEATURE,
new Identifier(MOD_ID, "overworld_portal"))
);
BackroomsBarrelManager.register();
}
// Blocks
public static final TrackerBlock TRACKER_BLOCK = Registry.register(