experimental popups fix and beer extension

This commit is contained in:
2026-03-21 13:31:13 +01:00
parent f44b59824f
commit fc50496c98
14 changed files with 446 additions and 163 deletions

View File

@@ -6,7 +6,7 @@ minecraft_version=1.20.1
yarn_mappings=1.20.1+build.10 yarn_mappings=1.20.1+build.10
loader_version=0.18.3 loader_version=0.18.3
# Mod Properties # Mod Properties
mod_version=26.3.20 mod_version=26.3.21
maven_group=dev.tggamesyt maven_group=dev.tggamesyt
archives_base_name=szar archives_base_name=szar
# Dependencies # Dependencies

View File

@@ -21,8 +21,11 @@ import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.lit
public class PanoramaClientCommand { public class PanoramaClientCommand {
static void register(CommandDispatcher<FabricClientCommandSource> dispatcher) { static void register(CommandDispatcher<FabricClientCommandSource> dispatcher) {
dispatcher.register(literal("takepanorama") dispatcher.register(
.executes(PanoramaClientCommand::execute)); literal("szar")
.then(literal("takepanorama")
.executes(PanoramaClientCommand::execute))
);
} }
private static int execute(CommandContext<FabricClientCommandSource> context) { private static int execute(CommandContext<FabricClientCommandSource> context) {

View File

@@ -93,6 +93,10 @@ public class SzarClient implements ClientModInitializer {
); );
@Override @Override
public void onInitializeClient() { public void onInitializeClient() {
ClientPlayNetworking.registerGlobalReceiver(Szar.DRUNK_TYPE_PACKET, (client, handler, buf, responseSender) -> {
String typeName = buf.readString();
client.execute(() -> DrunkEffect.setDisplayType(typeName));
});
SmilerEffectRenderer.register(); SmilerEffectRenderer.register();
EntityRendererRegistry.register(Szar.SMILER_ENTITY_TYPE, SmilerRenderer::new); EntityRendererRegistry.register(Szar.SMILER_ENTITY_TYPE, SmilerRenderer::new);
ClientPlayNetworking.registerGlobalReceiver(OPEN_DETONATOR_SCREEN, (client, handler, buf, responseSender) -> { ClientPlayNetworking.registerGlobalReceiver(OPEN_DETONATOR_SCREEN, (client, handler, buf, responseSender) -> {

View File

@@ -0,0 +1,34 @@
package dev.tggamesyt.szar.client.mixin;
import com.mojang.serialization.Lifecycle;
import net.minecraft.client.gui.screen.world.CreateWorldScreen;
import net.minecraft.client.world.GeneratorOptionsHolder;
import net.minecraft.world.gen.GeneratorOptions;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.ModifyArg;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
@Mixin(CreateWorldScreen.class)
public class CreateWorldScreenMixin {
/**
* Forces bypassWarnings=true on the tryLoad call inside createLevel(),
* so the experimental/deprecated world warning is never shown.
* The 5th parameter (index 4) of tryLoad is the boolean bypassWarnings.
*/
@ModifyArg(
method = "createLevel",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/server/integrated/IntegratedServerLoader;tryLoad(Lnet/minecraft/client/MinecraftClient;Lnet/minecraft/client/gui/screen/world/CreateWorldScreen;Lcom/mojang/serialization/Lifecycle;Ljava/lang/Runnable;Z)V"
),
index = 4
)
private boolean forceBypassWarnings(boolean original) {
return true;
}
}

View File

@@ -0,0 +1,34 @@
package dev.tggamesyt.szar.client.mixin;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.server.integrated.IntegratedServerLoader;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(IntegratedServerLoader.class)
public class IntegratedServerLoaderMixin {
@Final
@Shadow
private MinecraftClient client;
@Inject(
method = "showBackupPromptScreen",
at = @At("HEAD"),
cancellable = true
)
private void skipBackupPrompt(
Screen parent,
String levelName,
boolean customized,
Runnable callback,
CallbackInfo ci) {
this.client.send(callback);
ci.cancel();
}
}

View File

@@ -5,9 +5,11 @@
"compatibilityLevel": "JAVA_17", "compatibilityLevel": "JAVA_17",
"client": [ "client": [
"BipedEntityModelMixin", "BipedEntityModelMixin",
"CreateWorldScreenMixin",
"EntityRenderMixin", "EntityRenderMixin",
"GameRendererMixin", "GameRendererMixin",
"HeldItemRendererMixin", "HeldItemRendererMixin",
"IntegratedServerLoaderMixin",
"ItemRendererMixin", "ItemRendererMixin",
"LogoDrawerMixin", "LogoDrawerMixin",
"MouseFlipMixin", "MouseFlipMixin",

View File

@@ -38,7 +38,7 @@ public class BackroomsBarrelManager {
} }
private static void tick(MinecraftServer server) { private static void tick(MinecraftServer server) {
ServerWorld backrooms = server.getWorld(Szar.BACKROOMS_KEY); ServerWorld backrooms = server.getWorld(Szar.BACKROOMS_LEVEL_KEY);
if (backrooms == null) return; if (backrooms == null) return;
List<ServerPlayerEntity> players = backrooms.getPlayers(); List<ServerPlayerEntity> players = backrooms.getPlayers();

View File

@@ -31,7 +31,7 @@ public class BackroomsLightManager {
} }
private static void tick(MinecraftServer server) { private static void tick(MinecraftServer server) {
ServerWorld backrooms = server.getWorld(Szar.BACKROOMS_KEY); ServerWorld backrooms = server.getWorld(Szar.BACKROOMS_LEVEL_KEY);
if (backrooms == null) return; if (backrooms == null) return;
globalFlickerTimer++; globalFlickerTimer++;

View File

@@ -4,55 +4,209 @@ import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents;
import net.minecraft.entity.LivingEntity; import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.effect.StatusEffect; import net.minecraft.entity.effect.StatusEffect;
import net.minecraft.entity.effect.StatusEffectCategory; import net.minecraft.entity.effect.StatusEffectCategory;
import net.minecraft.entity.effect.StatusEffectInstance; import net.minecraft.entity.projectile.ProjectileUtil;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.item.ItemStack;
import net.minecraft.network.packet.s2c.play.EntityAnimationS2CPacket; import net.minecraft.network.packet.s2c.play.EntityAnimationS2CPacket;
import net.minecraft.server.MinecraftServer; import net.minecraft.server.MinecraftServer;
import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.sound.SoundCategory;
import net.minecraft.sound.SoundEvents;
import net.minecraft.text.Text;
import net.minecraft.util.Hand;
import net.minecraft.util.hit.EntityHitResult; import net.minecraft.util.hit.EntityHitResult;
import net.minecraft.util.hit.HitResult; import net.minecraft.util.math.Vec3d;
import net.minecraft.world.RaycastContext;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
public class DrunkEffect extends StatusEffect { public class DrunkEffect extends StatusEffect {
public enum DrunkType { AGGRESSIVE, STUMBLING, SLEEPY, GENEROUS, PARANOID }
// Per-player assigned drunk type — assigned when effect is first detected
private static final Map<UUID, DrunkType> playerTypes = new HashMap<>();
// For sleepy — track how long the player is forced to crouch
private static final Map<UUID, Integer> sleepyTimer = new HashMap<>();
static String currentDisplayType = "None";
public DrunkEffect() { public DrunkEffect() {
super(StatusEffectCategory.HARMFUL, 0xFF9900); super(StatusEffectCategory.HARMFUL, 0xFF9900);
} }
static void tick(MinecraftServer server) { static void tick(MinecraftServer server) {
for (var world : server.getWorlds()) { for (var world : server.getWorlds()) {
if (world.getTime() % 20 != 0) continue;
for (ServerPlayerEntity player : world.getPlayers()) { for (ServerPlayerEntity player : world.getPlayers()) {
if (!player.hasStatusEffect(Szar.DRUNK_EFFECT)) continue; if (!player.hasStatusEffect(Szar.DRUNK_EFFECT)) {
if (world.random.nextInt(5) >= 2) continue; // 2 in 5 chance // Clean up when effect ends
playerTypes.remove(player.getUuid());
sleepyTimer.remove(player.getUuid());
continue;
}
double reach = 4.5; // Assign type if not yet assigned
net.minecraft.util.math.Vec3d eyePos = player.getEyePos(); DrunkType type = playerTypes.computeIfAbsent(player.getUuid(), k -> {
net.minecraft.util.math.Vec3d lookVec = player.getRotationVector(); DrunkType[] values = DrunkType.values();
net.minecraft.util.math.Vec3d endPos = eyePos.add(lookVec.multiply(reach)); DrunkType assigned = values[world.random.nextInt(values.length)];
EntityHitResult entityHit = net.minecraft.entity.projectile.ProjectileUtil String typeName = assigned.name().charAt(0)
.getEntityCollision( + assigned.name().substring(1).toLowerCase();
world, net.minecraft.network.PacketByteBuf buf =
player, net.fabricmc.fabric.api.networking.v1.PacketByteBufs.create();
eyePos, buf.writeString(typeName);
endPos, net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking.send(
player.getBoundingBox().stretch(lookVec.multiply(reach)).expand(1.0), player, Szar.DRUNK_TYPE_PACKET, buf);
e -> e instanceof LivingEntity && e != player && !e.isSpectator()
);
if (entityHit == null) continue; return assigned;
if (!(entityHit.getEntity() instanceof LivingEntity target)) continue; });
player.attack(target); switch (type) {
player.swingHand(net.minecraft.util.Hand.MAIN_HAND); case AGGRESSIVE -> tickAggressive(world, player);
case STUMBLING -> tickStumbling(world, player);
EntityAnimationS2CPacket swingPacket = case SLEEPY -> tickSleepy(world, player);
new EntityAnimationS2CPacket( case GENEROUS -> tickGenerous(world, player);
player, 0); // 0 = swing main hand case PARANOID -> tickParanoid(world, player);
player.networkHandler.sendPacket(swingPacket); }
} }
} }
} }
}
// --- AGGRESSIVE ---
private static void tickAggressive(net.minecraft.server.world.ServerWorld world,
ServerPlayerEntity player) {
if (world.getTime() % 20 != 0) return;
if (world.random.nextInt(5) >= 2) return;
Vec3d eyePos = player.getEyePos();
Vec3d lookVec = player.getRotationVector();
Vec3d endPos = eyePos.add(lookVec.multiply(4.5));
EntityHitResult hit = ProjectileUtil.getEntityCollision(
world, player, eyePos, endPos,
player.getBoundingBox().stretch(lookVec.multiply(4.5)).expand(1.0),
e -> e instanceof LivingEntity && e != player && !e.isSpectator()
);
if (hit == null) return;
if (!(hit.getEntity() instanceof LivingEntity target)) return;
player.attack(target);
player.swingHand(Hand.MAIN_HAND);
player.networkHandler.sendPacket(new EntityAnimationS2CPacket(player, 0));
}
// --- STUMBLING ---
private static void tickStumbling(net.minecraft.server.world.ServerWorld world,
ServerPlayerEntity player) {
if (world.getTime() % 10 != 0) return;
if (world.random.nextInt(4) != 0) return;
// Add a random sideways velocity push
Vec3d current = player.getVelocity();
double pushX = (world.random.nextDouble() - 0.5) * 0.4;
double pushZ = (world.random.nextDouble() - 0.5) * 0.4;
player.setVelocity(current.x + pushX, current.y, current.z + pushZ);
player.velocityModified = true;
}
// --- SLEEPY ---
private static void tickSleepy(net.minecraft.server.world.ServerWorld world,
ServerPlayerEntity player) {
UUID uuid = player.getUuid();
int timer = sleepyTimer.getOrDefault(uuid, 0);
if (timer > 0) {
// Force crouch and stop movement
player.setVelocity(0, player.getVelocity().y, 0);
player.velocityModified = true;
player.setSneaking(true);
sleepyTimer.put(uuid, timer - 1);
} else {
player.setSneaking(false);
// Every 3 seconds, 1 in 4 chance to fall asleep for 1-3 seconds
if (world.getTime() % 60 == 0 && world.random.nextInt(4) == 0) {
int sleepDuration = 20 + world.random.nextInt(40); // 1-3 seconds
sleepyTimer.put(uuid, sleepDuration);
}
}
}
// --- GENEROUS ---
private static void tickGenerous(net.minecraft.server.world.ServerWorld world,
ServerPlayerEntity player) {
if (world.getTime() % 60 != 0) return; // every 3 seconds
if (world.random.nextInt(4) != 0) return;
// Find a random non-empty slot
var inv = player.getInventory();
java.util.List<Integer> nonEmpty = new java.util.ArrayList<>();
for (int i = 0; i < inv.size(); i++) {
if (!inv.getStack(i).isEmpty()) nonEmpty.add(i);
}
if (nonEmpty.isEmpty()) return;
int slot = nonEmpty.get(world.random.nextInt(nonEmpty.size()));
ItemStack stack = inv.getStack(slot);
// Drop one item from the stack
ItemStack toDrop = stack.copy();
toDrop.setCount(1);
stack.decrement(1);
if (stack.isEmpty()) inv.setStack(slot, ItemStack.EMPTY);
player.dropItem(toDrop, false);
}
// --- PARANOID ---
private static void tickParanoid(net.minecraft.server.world.ServerWorld world,
ServerPlayerEntity player) {
if (world.getTime() % 20 != 0) return;
if (world.random.nextInt(3) != 0) return;
// Play a footstep sound at a random position nearby
double offsetX = (world.random.nextDouble() - 0.5) * 10;
double offsetZ = (world.random.nextDouble() - 0.5) * 10;
double x = player.getX() + offsetX;
double y = player.getY();
double z = player.getZ() + offsetZ;
// Pick a random footstep sound
net.minecraft.sound.SoundEvent[] footsteps = {
SoundEvents.BLOCK_STONE_STEP,
SoundEvents.BLOCK_GRAVEL_STEP,
SoundEvents.BLOCK_WOOD_STEP,
SoundEvents.BLOCK_SAND_STEP
};
net.minecraft.sound.SoundEvent sound =
footsteps[world.random.nextInt(footsteps.length)];
// Play only to this player
player.networkHandler.sendPacket(
new net.minecraft.network.packet.s2c.play.PlaySoundS2CPacket(
world.getRegistryManager()
.get(net.minecraft.registry.RegistryKeys.SOUND_EVENT)
.getEntry(sound),
SoundCategory.PLAYERS,
x, y, z,
0.8f + world.random.nextFloat() * 0.4f,
0.8f + world.random.nextFloat() * 0.4f,
world.random.nextLong()
)
);
}
@Override
public Text getName() {
if (currentDisplayType.isEmpty()) return Text.literal("Drunk");
return Text.literal("Drunk ")
.append(Text.literal("(" + currentDisplayType + ")")
.formatted(net.minecraft.util.Formatting.GRAY));
}
public static void setDisplayType(String typeName) {
currentDisplayType = typeName;
}
public static void preAssignType(UUID uuid, DrunkType type) {
playerTypes.put(uuid, type);
}
}

View File

@@ -5,17 +5,13 @@ import net.minecraft.block.BlockState;
import net.minecraft.block.ShapeContext; import net.minecraft.block.ShapeContext;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
import net.minecraft.entity.ItemEntity; import net.minecraft.entity.ItemEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.player.PlayerInventory; import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NbtCompound; import net.minecraft.nbt.NbtCompound;
import net.minecraft.nbt.NbtList; import net.minecraft.nbt.NbtList;
import net.minecraft.registry.RegistryKey;
import net.minecraft.registry.RegistryKeys;
import net.minecraft.server.MinecraftServer; import net.minecraft.server.MinecraftServer;
import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.server.world.ServerWorld; import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.shape.VoxelShape; import net.minecraft.util.shape.VoxelShape;
import net.minecraft.util.shape.VoxelShapes; import net.minecraft.util.shape.VoxelShapes;
@@ -58,13 +54,13 @@ public class PortalBlock extends Block {
// Full player handling — inventory save, tracker registration, etc. // Full player handling — inventory save, tracker registration, etc.
if (world.getRegistryKey() == World.OVERWORLD) { if (world.getRegistryKey() == World.OVERWORLD) {
teleportToNether(player, tracker, server, pos); teleportToNether(player, tracker, server, pos);
} else if (world.getRegistryKey() == Szar.BACKROOMS_KEY) { } else if (world.getRegistryKey() == Szar.BACKROOMS_LEVEL_KEY) {
teleportToOverworld(player, tracker, server); teleportToOverworld(player, tracker, server);
} }
} else { } else {
// Non-player entity — teleport only, no inventory or tracker registration // Non-player entity — teleport only, no inventory or tracker registration
if (world.getRegistryKey() == World.OVERWORLD) { if (world.getRegistryKey() == World.OVERWORLD) {
ServerWorld backrooms = server.getWorld(Szar.BACKROOMS_KEY); ServerWorld backrooms = server.getWorld(Szar.BACKROOMS_LEVEL_KEY);
if (backrooms == null) return; if (backrooms == null) return;
// Save overworld entry coords for this entity // Save overworld entry coords for this entity
@@ -75,7 +71,7 @@ public class PortalBlock extends Block {
entity.teleport(backrooms, entity.getX(), safeY, entity.getZ(), entity.teleport(backrooms, entity.getX(), safeY, entity.getZ(),
java.util.Set.of(), entity.getYaw(), entity.getPitch()); java.util.Set.of(), entity.getYaw(), entity.getPitch());
} else if (world.getRegistryKey() == Szar.BACKROOMS_KEY) { } else if (world.getRegistryKey() == Szar.BACKROOMS_LEVEL_KEY) {
ServerWorld overworld = server.getWorld(World.OVERWORLD); ServerWorld overworld = server.getWorld(World.OVERWORLD);
if (overworld == null) return; if (overworld == null) return;
@@ -134,7 +130,7 @@ public class PortalBlock extends Block {
tracker.addPlayer(player.getUuid()); tracker.addPlayer(player.getUuid());
// Teleport // Teleport
ServerWorld nether = server.getWorld(Szar.BACKROOMS_KEY); ServerWorld nether = server.getWorld(Szar.BACKROOMS_LEVEL_KEY);
if (nether == null) return; if (nether == null) return;
double netherX = player.getX(); double netherX = player.getX();
@@ -179,7 +175,7 @@ public class PortalBlock extends Block {
netherTracker.removePlayer(player.getUuid()); netherTracker.removePlayer(player.getUuid());
ServerWorld overworld = server.getWorld(World.OVERWORLD); ServerWorld overworld = server.getWorld(World.OVERWORLD);
ServerWorld backrooms = server.getWorld(Szar.BACKROOMS_KEY); ServerWorld backrooms = server.getWorld(Szar.BACKROOMS_LEVEL_KEY);
if (owTrackerPos != null && overworld != null) { if (owTrackerPos != null && overworld != null) {
if (overworld.getBlockEntity(owTrackerPos) instanceof TrackerBlockEntity owTracker) { if (overworld.getBlockEntity(owTrackerPos) instanceof TrackerBlockEntity owTracker) {
@@ -307,7 +303,7 @@ public class PortalBlock extends Block {
private double findSafeY(ServerWorld world, int x, int z) { private double findSafeY(ServerWorld world, int x, int z) {
// For backrooms, search from y=8 downward only // For backrooms, search from y=8 downward only
int startY = world.getRegistryKey() == Szar.BACKROOMS_KEY ? 8 : 100; int startY = world.getRegistryKey() == Szar.BACKROOMS_LEVEL_KEY ? 8 : 100;
for (int y = startY; y > 1; y--) { for (int y = startY; y > 1; y--) {
BlockPos feet = new BlockPos(x, y, z); BlockPos feet = new BlockPos(x, y, z);
BlockPos head = feet.up(); BlockPos head = feet.up();

View File

@@ -26,7 +26,7 @@ public class SmilerSpawnManager {
} }
private static void tick(MinecraftServer server) { private static void tick(MinecraftServer server) {
ServerWorld backrooms = server.getWorld(Szar.BACKROOMS_KEY); ServerWorld backrooms = server.getWorld(Szar.BACKROOMS_LEVEL_KEY);
if (backrooms == null) return; if (backrooms == null) return;
// Only spawn during blackout // Only spawn during blackout

View File

@@ -14,7 +14,6 @@ import net.fabricmc.fabric.api.event.player.AttackEntityCallback;
import net.fabricmc.fabric.api.event.player.UseBlockCallback; import net.fabricmc.fabric.api.event.player.UseBlockCallback;
import net.fabricmc.fabric.api.item.v1.FabricItemSettings; import net.fabricmc.fabric.api.item.v1.FabricItemSettings;
import net.fabricmc.fabric.api.itemgroup.v1.FabricItemGroup; import net.fabricmc.fabric.api.itemgroup.v1.FabricItemGroup;
import net.fabricmc.fabric.api.loot.v2.LootTableEvents;
import net.fabricmc.fabric.api.message.v1.ServerMessageDecoratorEvent; import net.fabricmc.fabric.api.message.v1.ServerMessageDecoratorEvent;
import net.fabricmc.fabric.api.networking.v1.PacketByteBufs; import net.fabricmc.fabric.api.networking.v1.PacketByteBufs;
import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents; import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents;
@@ -43,12 +42,7 @@ import net.minecraft.entity.effect.StatusEffect;
import net.minecraft.entity.effect.StatusEffectInstance; import net.minecraft.entity.effect.StatusEffectInstance;
import net.minecraft.entity.passive.VillagerEntity; import net.minecraft.entity.passive.VillagerEntity;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.projectile.ProjectileEntity;
import net.minecraft.item.*; import net.minecraft.item.*;
import net.minecraft.loot.LootPool;
import net.minecraft.loot.entry.ItemEntry;
import net.minecraft.loot.function.SetCountLootFunction;
import net.minecraft.loot.provider.number.ConstantLootNumberProvider;
import net.minecraft.nbt.NbtCompound; import net.minecraft.nbt.NbtCompound;
import net.minecraft.network.PacketByteBuf; import net.minecraft.network.PacketByteBuf;
import net.minecraft.registry.*; import net.minecraft.registry.*;
@@ -71,17 +65,16 @@ import net.minecraft.util.ActionResult;
import net.minecraft.util.Formatting; import net.minecraft.util.Formatting;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
import net.minecraft.util.Rarity; import net.minecraft.util.Rarity;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.hit.HitResult;
import net.minecraft.util.math.*; import net.minecraft.util.math.*;
import net.minecraft.util.shape.VoxelShape; import net.minecraft.util.shape.VoxelShape;
import net.minecraft.util.shape.VoxelShapes; import net.minecraft.util.shape.VoxelShapes;
import net.minecraft.village.TradeOffer; import net.minecraft.village.TradeOffer;
import net.minecraft.village.VillagerProfession; import net.minecraft.village.VillagerProfession;
import net.minecraft.world.Heightmap; import net.minecraft.world.Heightmap;
import net.minecraft.world.RaycastContext;
import net.minecraft.world.World; import net.minecraft.world.World;
import net.minecraft.world.biome.BiomeKeys; import net.minecraft.world.biome.BiomeKeys;
import net.minecraft.world.dimension.DimensionOptions;
import net.minecraft.world.dimension.DimensionType;
import net.minecraft.world.gen.GenerationStep; import net.minecraft.world.gen.GenerationStep;
import net.minecraft.world.gen.YOffset; import net.minecraft.world.gen.YOffset;
import net.minecraft.world.gen.feature.*; import net.minecraft.world.gen.feature.*;
@@ -93,9 +86,10 @@ import net.minecraft.world.gen.structure.StructureType;
import net.minecraft.world.poi.PointOfInterestType; import net.minecraft.world.poi.PointOfInterestType;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.core.jmx.Server;
import java.awt.*;
import java.util.*; import java.util.*;
import java.util.List;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@@ -106,6 +100,7 @@ public class Szar implements ModInitializer {
public static final String MOD_ID = "szar"; public static final String MOD_ID = "szar";
public static final Logger LOGGER = LogManager.getLogger(MOD_ID); public static final Logger LOGGER = LogManager.getLogger(MOD_ID);
public static MinecraftServer SERVER; public static MinecraftServer SERVER;
public static final Identifier DRUNK_TYPE_PACKET = new Identifier(MOD_ID, "drunk_type");
public static final Identifier OPEN_DETONATOR_SCREEN = new Identifier(MOD_ID, "open_coord_screen"); public static final Identifier OPEN_DETONATOR_SCREEN = new Identifier(MOD_ID, "open_coord_screen");
public static final Identifier DETONATOR_INPUT = new Identifier(MOD_ID, "coord_input"); public static final Identifier DETONATOR_INPUT = new Identifier(MOD_ID, "coord_input");
public static final Identifier REVOLVER_SHOOT = new Identifier(MOD_ID, "revolver_shoot"); public static final Identifier REVOLVER_SHOOT = new Identifier(MOD_ID, "revolver_shoot");
@@ -174,11 +169,18 @@ public class Szar implements ModInitializer {
public static final Identifier PLAY_VIDEO = public static final Identifier PLAY_VIDEO =
new Identifier(MOD_ID, "play_video"); new Identifier(MOD_ID, "play_video");
public static final Identifier CONFIG_SYNC = new Identifier(MOD_ID, "config_sync"); public static final Identifier CONFIG_SYNC = new Identifier(MOD_ID, "config_sync");
public static final RegistryKey<DimensionOptions> BACKROOMS_KEY = RegistryKey.of(
public static final RegistryKey<World> BACKROOMS_KEY = RegistryKey.of( RegistryKeys.DIMENSION,
new Identifier(MOD_ID, "backrooms")
);
public static final RegistryKey<World> BACKROOMS_LEVEL_KEY = RegistryKey.of(
RegistryKeys.WORLD, RegistryKeys.WORLD,
new Identifier(MOD_ID, "backrooms") new Identifier(MOD_ID, "backrooms")
); );
public static final RegistryKey<DimensionType> BACKROOMS_DIM_TYPE = RegistryKey.of(
RegistryKeys.DIMENSION_TYPE,
new Identifier(MOD_ID, "backrooms")
);
public static final Block SZAR_BLOCK = public static final Block SZAR_BLOCK =
new SzarBlock(); new SzarBlock();
public static final Block URANIUM_BLOCK = public static final Block URANIUM_BLOCK =
@@ -946,121 +948,138 @@ public class Szar implements ModInitializer {
}); });
CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> { CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> {
dispatcher.register( dispatcher.register(
LiteralArgumentBuilder.<ServerCommandSource>literal("ny") LiteralArgumentBuilder.<ServerCommandSource>literal("szar")
.requires(context -> context.hasPermissionLevel(2)) .requires(context -> context.hasPermissionLevel(2))
.executes(context -> {
ServerCommandSource source = context.getSource();
ServerWorld world = source.getWorld();
// Kill all KidEntity instances // /szar ny
int count = world.getEntitiesByType(NyanEntityType, e -> true).size(); .then(CommandManager.literal("ny")
world.getEntitiesByType(NyanEntityType, e -> true).forEach(e -> e.kill());
source.sendMessage(Text.literal("Killed " + count + " nyan cats."));
return count;
})
);
dispatcher.register(
LiteralArgumentBuilder.<ServerCommandSource>literal("getnearestobeliskcore")
.requires(context -> context.hasPermissionLevel(2))
.executes(context -> {
ServerCommandSource source = context.getSource();
ServerWorld world = source.getWorld();
assert source.getEntity() != null;
ObeliskCoreBlockEntity nearest = findNearestObelisk(world, source.getEntity().getBlockPos(), 100);
if (nearest != null) {
boolean hasPlane = nearest.hasPlaneMob();
source.sendMessage(Text.literal(
"HasPlane: " + hasPlane
));
return 1;
}
return 0;
})
);
dispatcher.register(
LiteralArgumentBuilder.<ServerCommandSource>literal("backroomlights")
.requires(context -> context.hasPermissionLevel(2))
.then(CommandManager.literal("get")
.executes(context -> { .executes(context -> {
ServerCommandSource source = context.getSource(); ServerCommandSource source = context.getSource();
BackroomsLightManager.GlobalEvent event = BackroomsLightManager.currentEvent; ServerWorld world = source.getWorld();
int timer = BackroomsLightManager.eventTimer; int count = world.getEntitiesByType(NyanEntityType, e -> true).size();
world.getEntitiesByType(NyanEntityType, e -> true).forEach(e -> e.kill());
String mode = switch (event) { source.sendMessage(Text.literal("Killed " + count + " nyan cats."));
case NONE -> "normal"; return count;
case FLICKER -> "flickering";
case BLACKOUT -> "blackout";
};
if (event == BackroomsLightManager.GlobalEvent.NONE) {
int cooldown = BackroomsLightManager.cooldownTimer;
source.sendMessage(Text.literal(
"Current mode: §anormal§r — next event check in §e"
+ cooldown + "§r ticks ("
+ (cooldown / 20) + "s)"
));
} else {
source.sendMessage(Text.literal(
"Current mode: §e" + mode + "§r — ends in §c"
+ timer + "§r ticks ("
+ (timer / 20) + "s)"
));
}
return 1;
}) })
) )
.then(CommandManager.literal("set")
.then(CommandManager.literal("normal") // /szar getnearestobeliskcore
.then(CommandManager.literal("getnearestobeliskcore")
.executes(context -> {
ServerCommandSource source = context.getSource();
ServerWorld world = source.getWorld();
assert source.getEntity() != null;
ObeliskCoreBlockEntity nearest = findNearestObelisk(world,
source.getEntity().getBlockPos(), 100);
if (nearest != null) {
boolean hasPlane = nearest.hasPlaneMob();
source.sendMessage(Text.literal("HasPlane: " + hasPlane));
return 1;
}
return 0;
})
)
// /szar backroomlights get/set
.then(CommandManager.literal("backroomlights")
.then(CommandManager.literal("get")
.executes(context -> { .executes(context -> {
ServerCommandSource source = context.getSource(); ServerCommandSource source = context.getSource();
ServerWorld backrooms = source.getServer().getWorld(Szar.BACKROOMS_KEY); BackroomsLightManager.GlobalEvent event = BackroomsLightManager.currentEvent;
if (backrooms == null) { int timer = BackroomsLightManager.eventTimer;
source.sendError(Text.literal("Backrooms dimension not found")); String mode = switch (event) {
return 0; case NONE -> "normal";
case FLICKER -> "flickering";
case BLACKOUT -> "blackout";
};
if (event == BackroomsLightManager.GlobalEvent.NONE) {
int cooldown = BackroomsLightManager.cooldownTimer;
source.sendMessage(Text.literal(
"Current mode: §anormal§r — next event check in §e"
+ cooldown + "§r ticks (" + (cooldown / 20) + "s)"));
} else {
source.sendMessage(Text.literal(
"Current mode: §e" + mode + "§r — ends in §c"
+ timer + "§r ticks (" + (timer / 20) + "s)"));
} }
// End current event cleanly
BackroomsLightManager.currentEvent = BackroomsLightManager.GlobalEvent.NONE;
BackroomsLightManager.eventTimer = 0;
BackroomsLightManager.cooldownTimer = 3600;
// Restore all lights
BackroomsLightManager.forceRestoreAllLights(backrooms);
source.sendMessage(Text.literal("§aBackrooms lights set to normal"));
return 1; return 1;
}) })
) )
.then(CommandManager.literal("flickering") .then(CommandManager.literal("set")
.executes(context -> { .then(CommandManager.literal("normal")
ServerCommandSource source = context.getSource(); .executes(context -> {
ServerWorld backrooms = source.getServer().getWorld(Szar.BACKROOMS_KEY); ServerCommandSource source = context.getSource();
if (backrooms == null) { ServerWorld backrooms = source.getServer().getWorld(Szar.BACKROOMS_LEVEL_KEY);
source.sendError(Text.literal("Backrooms dimension not found")); if (backrooms == null) {
return 0; source.sendError(Text.literal("Backrooms dimension not found"));
} return 0;
BackroomsLightManager.currentEvent = BackroomsLightManager.GlobalEvent.FLICKER; }
BackroomsLightManager.eventTimer = 3600; // 3 minutes default BackroomsLightManager.currentEvent = BackroomsLightManager.GlobalEvent.NONE;
BackroomsLightManager.cooldownTimer = 3600; BackroomsLightManager.eventTimer = 0;
source.sendMessage(Text.literal("§eBackrooms lights set to flickering")); BackroomsLightManager.cooldownTimer = 3600;
return 1; BackroomsLightManager.forceRestoreAllLights(backrooms);
}) source.sendMessage(Text.literal("§aBackrooms lights set to normal"));
return 1;
})
)
.then(CommandManager.literal("flickering")
.executes(context -> {
ServerCommandSource source = context.getSource();
ServerWorld backrooms = source.getServer().getWorld(Szar.BACKROOMS_LEVEL_KEY);
if (backrooms == null) {
source.sendError(Text.literal("Backrooms dimension not found"));
return 0;
}
BackroomsLightManager.currentEvent = BackroomsLightManager.GlobalEvent.FLICKER;
BackroomsLightManager.eventTimer = 3600;
BackroomsLightManager.cooldownTimer = 3600;
source.sendMessage(Text.literal("§eBackrooms lights set to flickering"));
return 1;
})
)
.then(CommandManager.literal("blackout")
.executes(context -> {
ServerCommandSource source = context.getSource();
ServerWorld backrooms = source.getServer().getWorld(Szar.BACKROOMS_LEVEL_KEY);
if (backrooms == null) {
source.sendError(Text.literal("Backrooms dimension not found"));
return 0;
}
BackroomsLightManager.currentEvent = BackroomsLightManager.GlobalEvent.BLACKOUT;
BackroomsLightManager.eventTimer = 3600;
BackroomsLightManager.cooldownTimer = 3600;
BackroomsLightManager.forceBlackout(backrooms);
source.sendMessage(Text.literal("§4Backrooms lights set to blackout"));
return 1;
})
)
) )
.then(CommandManager.literal("blackout") )
.executes(context -> {
ServerCommandSource source = context.getSource(); // /szar drunk <targets> <type>
ServerWorld backrooms = source.getServer().getWorld(Szar.BACKROOMS_KEY); .then(CommandManager.literal("drunk")
if (backrooms == null) { .then(CommandManager.argument("targets",
source.sendError(Text.literal("Backrooms dimension not found")); net.minecraft.command.argument.EntityArgumentType.players())
return 0; .then(CommandManager.literal("aggressive")
} .executes(ctx -> applyDrunk(ctx,
BackroomsLightManager.currentEvent = BackroomsLightManager.GlobalEvent.BLACKOUT; net.minecraft.command.argument.EntityArgumentType.getPlayers(ctx, "targets"),
BackroomsLightManager.eventTimer = 3600; DrunkEffect.DrunkType.AGGRESSIVE)))
BackroomsLightManager.cooldownTimer = 3600; .then(CommandManager.literal("stumbling")
BackroomsLightManager.forceBlackout(backrooms); .executes(ctx -> applyDrunk(ctx,
source.sendMessage(Text.literal("§4Backrooms lights set to blackout")); net.minecraft.command.argument.EntityArgumentType.getPlayers(ctx, "targets"),
return 1; DrunkEffect.DrunkType.STUMBLING)))
}) .then(CommandManager.literal("sleepy")
.executes(ctx -> applyDrunk(ctx,
net.minecraft.command.argument.EntityArgumentType.getPlayers(ctx, "targets"),
DrunkEffect.DrunkType.SLEEPY)))
.then(CommandManager.literal("generous")
.executes(ctx -> applyDrunk(ctx,
net.minecraft.command.argument.EntityArgumentType.getPlayers(ctx, "targets"),
DrunkEffect.DrunkType.GENEROUS)))
.then(CommandManager.literal("paranoid")
.executes(ctx -> applyDrunk(ctx,
net.minecraft.command.argument.EntityArgumentType.getPlayers(ctx, "targets"),
DrunkEffect.DrunkType.PARANOID)))
) )
) )
); );
@@ -1186,7 +1205,7 @@ public class Szar implements ModInitializer {
SmilerSpawnManager.register(); SmilerSpawnManager.register();
// 🔄 Dimension change // 🔄 Dimension change
ServerEntityWorldChangeEvents.AFTER_PLAYER_CHANGE_WORLD.register((player, origin, destination) -> { ServerEntityWorldChangeEvents.AFTER_PLAYER_CHANGE_WORLD.register((player, origin, destination) -> {
if (origin.getRegistryKey() == Szar.BACKROOMS_KEY) { if (origin.getRegistryKey() == Szar.BACKROOMS_LEVEL_KEY) {
restoreIfNeeded(player); restoreIfNeeded(player);
} }
}); });
@@ -1202,7 +1221,7 @@ public class Szar implements ModInitializer {
ServerPlayConnectionEvents.JOIN.register((handler, sender, server) -> { ServerPlayConnectionEvents.JOIN.register((handler, sender, server) -> {
ServerPlayerEntity player = handler.getPlayer(); ServerPlayerEntity player = handler.getPlayer();
if (player.getWorld().getRegistryKey() != Szar.BACKROOMS_KEY) { if (player.getWorld().getRegistryKey() != Szar.BACKROOMS_LEVEL_KEY) {
restoreIfNeeded(player); restoreIfNeeded(player);
} }
}); });
@@ -2267,5 +2286,26 @@ public class Szar implements ModInitializer {
tag.remove("OwnerTrackerY"); tag.remove("OwnerTrackerY");
tag.remove("OwnerTrackerZ"); tag.remove("OwnerTrackerZ");
} }
private static int applyDrunk(com.mojang.brigadier.context.CommandContext<ServerCommandSource> ctx,
java.util.Collection<ServerPlayerEntity> players,
DrunkEffect.DrunkType type) {
for (ServerPlayerEntity player : players) {
// Pre-assign the type before applying effect so tick() doesn't randomize it
DrunkEffect.preAssignType(player.getUuid(), type);
player.addStatusEffect(new net.minecraft.entity.effect.StatusEffectInstance(
Szar.DRUNK_EFFECT, 2400, 0, false, true, true));
// Sync type to client immediately
String typeName = type.name().charAt(0) + type.name().substring(1).toLowerCase();
net.minecraft.network.PacketByteBuf buf =
net.fabricmc.fabric.api.networking.v1.PacketByteBufs.create();
buf.writeString(typeName);
net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking.send(
player, Szar.DRUNK_TYPE_PACKET, buf);
}
return players.size();
}
} }

View File

@@ -0,0 +1,15 @@
package dev.tggamesyt.szar.mixin;
import net.minecraft.world.level.storage.LevelSummary;
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(value = LevelSummary.class, priority = 1001)
public class LevelSummaryMixin {
@Inject(method = "isExperimental", at = @At(value = "RETURN"), cancellable = true)
public void isExperimental(CallbackInfoReturnable<Boolean> cir) {
cir.setReturnValue(false);
}
}

View File

@@ -6,6 +6,7 @@
"mixins": [ "mixins": [
"CraftingScreenHandlerMixin", "CraftingScreenHandlerMixin",
"CraftingScreenHandlerMixin2", "CraftingScreenHandlerMixin2",
"LevelSummaryMixin",
"LivingEntityFallDamageMixin", "LivingEntityFallDamageMixin",
"NoClipMixin", "NoClipMixin",
"PlaneBlockInteractionMixin", "PlaneBlockInteractionMixin",