diff --git a/gradle.properties b/gradle.properties index 367a988..e830541 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.20 +mod_version=26.3.21 maven_group=dev.tggamesyt archives_base_name=szar # Dependencies diff --git a/src/client/java/dev/tggamesyt/szar/client/PanoramaClientCommand.java b/src/client/java/dev/tggamesyt/szar/client/PanoramaClientCommand.java index dc79a46..198cd89 100644 --- a/src/client/java/dev/tggamesyt/szar/client/PanoramaClientCommand.java +++ b/src/client/java/dev/tggamesyt/szar/client/PanoramaClientCommand.java @@ -21,8 +21,11 @@ import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.lit public class PanoramaClientCommand { static void register(CommandDispatcher dispatcher) { - dispatcher.register(literal("takepanorama") - .executes(PanoramaClientCommand::execute)); + dispatcher.register( + literal("szar") + .then(literal("takepanorama") + .executes(PanoramaClientCommand::execute)) + ); } private static int execute(CommandContext context) { diff --git a/src/client/java/dev/tggamesyt/szar/client/SzarClient.java b/src/client/java/dev/tggamesyt/szar/client/SzarClient.java index e78fa84..8c5ade8 100644 --- a/src/client/java/dev/tggamesyt/szar/client/SzarClient.java +++ b/src/client/java/dev/tggamesyt/szar/client/SzarClient.java @@ -93,6 +93,10 @@ public class SzarClient implements ClientModInitializer { ); @Override public void onInitializeClient() { + ClientPlayNetworking.registerGlobalReceiver(Szar.DRUNK_TYPE_PACKET, (client, handler, buf, responseSender) -> { + String typeName = buf.readString(); + client.execute(() -> DrunkEffect.setDisplayType(typeName)); + }); SmilerEffectRenderer.register(); EntityRendererRegistry.register(Szar.SMILER_ENTITY_TYPE, SmilerRenderer::new); ClientPlayNetworking.registerGlobalReceiver(OPEN_DETONATOR_SCREEN, (client, handler, buf, responseSender) -> { diff --git a/src/client/java/dev/tggamesyt/szar/client/mixin/CreateWorldScreenMixin.java b/src/client/java/dev/tggamesyt/szar/client/mixin/CreateWorldScreenMixin.java new file mode 100644 index 0000000..2c4d1db --- /dev/null +++ b/src/client/java/dev/tggamesyt/szar/client/mixin/CreateWorldScreenMixin.java @@ -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; + } +} diff --git a/src/client/java/dev/tggamesyt/szar/client/mixin/IntegratedServerLoaderMixin.java b/src/client/java/dev/tggamesyt/szar/client/mixin/IntegratedServerLoaderMixin.java new file mode 100644 index 0000000..7348972 --- /dev/null +++ b/src/client/java/dev/tggamesyt/szar/client/mixin/IntegratedServerLoaderMixin.java @@ -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(); + } +} \ No newline at end of file diff --git a/src/client/resources/szar.client.mixins.json b/src/client/resources/szar.client.mixins.json index da7352b..b9a6424 100644 --- a/src/client/resources/szar.client.mixins.json +++ b/src/client/resources/szar.client.mixins.json @@ -5,9 +5,11 @@ "compatibilityLevel": "JAVA_17", "client": [ "BipedEntityModelMixin", + "CreateWorldScreenMixin", "EntityRenderMixin", "GameRendererMixin", "HeldItemRendererMixin", + "IntegratedServerLoaderMixin", "ItemRendererMixin", "LogoDrawerMixin", "MouseFlipMixin", diff --git a/src/main/java/dev/tggamesyt/szar/BackroomsBarrelManager.java b/src/main/java/dev/tggamesyt/szar/BackroomsBarrelManager.java index ee01c7b..903e6b1 100644 --- a/src/main/java/dev/tggamesyt/szar/BackroomsBarrelManager.java +++ b/src/main/java/dev/tggamesyt/szar/BackroomsBarrelManager.java @@ -38,7 +38,7 @@ public class BackroomsBarrelManager { } 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; List players = backrooms.getPlayers(); diff --git a/src/main/java/dev/tggamesyt/szar/BackroomsLightManager.java b/src/main/java/dev/tggamesyt/szar/BackroomsLightManager.java index 6b9c2b4..877bfa4 100644 --- a/src/main/java/dev/tggamesyt/szar/BackroomsLightManager.java +++ b/src/main/java/dev/tggamesyt/szar/BackroomsLightManager.java @@ -31,7 +31,7 @@ public class BackroomsLightManager { } 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; globalFlickerTimer++; diff --git a/src/main/java/dev/tggamesyt/szar/DrunkEffect.java b/src/main/java/dev/tggamesyt/szar/DrunkEffect.java index 75ab0c6..fe6434e 100644 --- a/src/main/java/dev/tggamesyt/szar/DrunkEffect.java +++ b/src/main/java/dev/tggamesyt/szar/DrunkEffect.java @@ -4,55 +4,209 @@ import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents; import net.minecraft.entity.LivingEntity; import net.minecraft.entity.effect.StatusEffect; import net.minecraft.entity.effect.StatusEffectCategory; -import net.minecraft.entity.effect.StatusEffectInstance; -import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.entity.projectile.ProjectileUtil; +import net.minecraft.item.ItemStack; import net.minecraft.network.packet.s2c.play.EntityAnimationS2CPacket; import net.minecraft.server.MinecraftServer; 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.HitResult; -import net.minecraft.world.RaycastContext; +import net.minecraft.util.math.Vec3d; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; 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 playerTypes = new HashMap<>(); + // For sleepy — track how long the player is forced to crouch + private static final Map sleepyTimer = new HashMap<>(); + static String currentDisplayType = "None"; public DrunkEffect() { super(StatusEffectCategory.HARMFUL, 0xFF9900); } static void tick(MinecraftServer server) { for (var world : server.getWorlds()) { - if (world.getTime() % 20 != 0) continue; - for (ServerPlayerEntity player : world.getPlayers()) { - if (!player.hasStatusEffect(Szar.DRUNK_EFFECT)) continue; - if (world.random.nextInt(5) >= 2) continue; // 2 in 5 chance + if (!player.hasStatusEffect(Szar.DRUNK_EFFECT)) { + // Clean up when effect ends + playerTypes.remove(player.getUuid()); + sleepyTimer.remove(player.getUuid()); + continue; + } - double reach = 4.5; - net.minecraft.util.math.Vec3d eyePos = player.getEyePos(); - net.minecraft.util.math.Vec3d lookVec = player.getRotationVector(); - net.minecraft.util.math.Vec3d endPos = eyePos.add(lookVec.multiply(reach)); + // Assign type if not yet assigned + DrunkType type = playerTypes.computeIfAbsent(player.getUuid(), k -> { + DrunkType[] values = DrunkType.values(); + DrunkType assigned = values[world.random.nextInt(values.length)]; - EntityHitResult entityHit = net.minecraft.entity.projectile.ProjectileUtil - .getEntityCollision( - world, - player, - eyePos, - endPos, - player.getBoundingBox().stretch(lookVec.multiply(reach)).expand(1.0), - e -> e instanceof LivingEntity && e != player && !e.isSpectator() - ); + String typeName = assigned.name().charAt(0) + + assigned.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); - if (entityHit == null) continue; - if (!(entityHit.getEntity() instanceof LivingEntity target)) continue; + return assigned; + }); - player.attack(target); - player.swingHand(net.minecraft.util.Hand.MAIN_HAND); - - EntityAnimationS2CPacket swingPacket = - new EntityAnimationS2CPacket( - player, 0); // 0 = swing main hand - player.networkHandler.sendPacket(swingPacket); + switch (type) { + case AGGRESSIVE -> tickAggressive(world, player); + case STUMBLING -> tickStumbling(world, player); + case SLEEPY -> tickSleepy(world, player); + case GENEROUS -> tickGenerous(world, player); + case PARANOID -> tickParanoid(world, player); + } } } } -} \ No newline at end of file + + // --- 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 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); + } +} diff --git a/src/main/java/dev/tggamesyt/szar/PortalBlock.java b/src/main/java/dev/tggamesyt/szar/PortalBlock.java index 5c89f7b..5170cef 100644 --- a/src/main/java/dev/tggamesyt/szar/PortalBlock.java +++ b/src/main/java/dev/tggamesyt/szar/PortalBlock.java @@ -5,17 +5,13 @@ import net.minecraft.block.BlockState; import net.minecraft.block.ShapeContext; import net.minecraft.entity.Entity; import net.minecraft.entity.ItemEntity; -import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerInventory; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NbtCompound; import net.minecraft.nbt.NbtList; -import net.minecraft.registry.RegistryKey; -import net.minecraft.registry.RegistryKeys; import net.minecraft.server.MinecraftServer; import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.server.world.ServerWorld; -import net.minecraft.util.Identifier; import net.minecraft.util.math.BlockPos; import net.minecraft.util.shape.VoxelShape; import net.minecraft.util.shape.VoxelShapes; @@ -58,13 +54,13 @@ public class PortalBlock extends Block { // 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) { + } else if (world.getRegistryKey() == Szar.BACKROOMS_LEVEL_KEY) { teleportToOverworld(player, tracker, server); } } else { // Non-player entity — teleport only, no inventory or tracker registration if (world.getRegistryKey() == World.OVERWORLD) { - ServerWorld backrooms = server.getWorld(Szar.BACKROOMS_KEY); + ServerWorld backrooms = server.getWorld(Szar.BACKROOMS_LEVEL_KEY); if (backrooms == null) return; // Save overworld entry coords for this entity @@ -75,7 +71,7 @@ public class PortalBlock extends Block { entity.teleport(backrooms, entity.getX(), safeY, entity.getZ(), 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); if (overworld == null) return; @@ -134,7 +130,7 @@ public class PortalBlock extends Block { tracker.addPlayer(player.getUuid()); // Teleport - ServerWorld nether = server.getWorld(Szar.BACKROOMS_KEY); + ServerWorld nether = server.getWorld(Szar.BACKROOMS_LEVEL_KEY); if (nether == null) return; double netherX = player.getX(); @@ -179,7 +175,7 @@ public class PortalBlock extends Block { netherTracker.removePlayer(player.getUuid()); 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 (overworld.getBlockEntity(owTrackerPos) instanceof TrackerBlockEntity owTracker) { @@ -307,7 +303,7 @@ public class PortalBlock extends Block { private double findSafeY(ServerWorld world, int x, int z) { // 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--) { BlockPos feet = new BlockPos(x, y, z); BlockPos head = feet.up(); diff --git a/src/main/java/dev/tggamesyt/szar/SmilerSpawnManager.java b/src/main/java/dev/tggamesyt/szar/SmilerSpawnManager.java index 76aedcf..39ecc7d 100644 --- a/src/main/java/dev/tggamesyt/szar/SmilerSpawnManager.java +++ b/src/main/java/dev/tggamesyt/szar/SmilerSpawnManager.java @@ -26,7 +26,7 @@ public class SmilerSpawnManager { } 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; // Only spawn during blackout diff --git a/src/main/java/dev/tggamesyt/szar/Szar.java b/src/main/java/dev/tggamesyt/szar/Szar.java index 42484f1..7ab7b51 100644 --- a/src/main/java/dev/tggamesyt/szar/Szar.java +++ b/src/main/java/dev/tggamesyt/szar/Szar.java @@ -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.item.v1.FabricItemSettings; 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.networking.v1.PacketByteBufs; 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.passive.VillagerEntity; import net.minecraft.entity.player.PlayerEntity; -import net.minecraft.entity.projectile.ProjectileEntity; 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.network.PacketByteBuf; import net.minecraft.registry.*; @@ -71,17 +65,16 @@ import net.minecraft.util.ActionResult; import net.minecraft.util.Formatting; import net.minecraft.util.Identifier; 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.shape.VoxelShape; import net.minecraft.util.shape.VoxelShapes; import net.minecraft.village.TradeOffer; import net.minecraft.village.VillagerProfession; import net.minecraft.world.Heightmap; -import net.minecraft.world.RaycastContext; import net.minecraft.world.World; 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.YOffset; import net.minecraft.world.gen.feature.*; @@ -93,9 +86,10 @@ import net.minecraft.world.gen.structure.StructureType; import net.minecraft.world.poi.PointOfInterestType; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.apache.logging.log4j.core.jmx.Server; +import java.awt.*; import java.util.*; +import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.regex.Pattern; @@ -106,6 +100,7 @@ public class Szar implements ModInitializer { public static final String MOD_ID = "szar"; public static final Logger LOGGER = LogManager.getLogger(MOD_ID); 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 DETONATOR_INPUT = new Identifier(MOD_ID, "coord_input"); 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 = new Identifier(MOD_ID, "play_video"); public static final Identifier CONFIG_SYNC = new Identifier(MOD_ID, "config_sync"); - - public static final RegistryKey BACKROOMS_KEY = RegistryKey.of( + public static final RegistryKey BACKROOMS_KEY = RegistryKey.of( + RegistryKeys.DIMENSION, + new Identifier(MOD_ID, "backrooms") + ); + public static final RegistryKey BACKROOMS_LEVEL_KEY = RegistryKey.of( RegistryKeys.WORLD, new Identifier(MOD_ID, "backrooms") ); + public static final RegistryKey BACKROOMS_DIM_TYPE = RegistryKey.of( + RegistryKeys.DIMENSION_TYPE, + new Identifier(MOD_ID, "backrooms") + ); public static final Block SZAR_BLOCK = new SzarBlock(); public static final Block URANIUM_BLOCK = @@ -946,121 +948,138 @@ public class Szar implements ModInitializer { }); CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> { dispatcher.register( - LiteralArgumentBuilder.literal("ny") + LiteralArgumentBuilder.literal("szar") .requires(context -> context.hasPermissionLevel(2)) - .executes(context -> { - ServerCommandSource source = context.getSource(); - ServerWorld world = source.getWorld(); - // Kill all KidEntity instances - int count = world.getEntitiesByType(NyanEntityType, e -> true).size(); - world.getEntitiesByType(NyanEntityType, e -> true).forEach(e -> e.kill()); - - source.sendMessage(Text.literal("Killed " + count + " nyan cats.")); - return count; - }) - ); - dispatcher.register( - LiteralArgumentBuilder.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.literal("backroomlights") - .requires(context -> context.hasPermissionLevel(2)) - .then(CommandManager.literal("get") + // /szar ny + .then(CommandManager.literal("ny") .executes(context -> { ServerCommandSource source = context.getSource(); - BackroomsLightManager.GlobalEvent event = BackroomsLightManager.currentEvent; - int timer = BackroomsLightManager.eventTimer; - - String mode = switch (event) { - 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)" - )); - } - return 1; + ServerWorld world = source.getWorld(); + int count = world.getEntitiesByType(NyanEntityType, e -> true).size(); + world.getEntitiesByType(NyanEntityType, e -> true).forEach(e -> e.kill()); + source.sendMessage(Text.literal("Killed " + count + " nyan cats.")); + return count; }) ) - .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 -> { ServerCommandSource source = context.getSource(); - ServerWorld backrooms = source.getServer().getWorld(Szar.BACKROOMS_KEY); - if (backrooms == null) { - source.sendError(Text.literal("Backrooms dimension not found")); - return 0; + BackroomsLightManager.GlobalEvent event = BackroomsLightManager.currentEvent; + int timer = BackroomsLightManager.eventTimer; + String mode = switch (event) { + 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; }) ) - .then(CommandManager.literal("flickering") - .executes(context -> { - ServerCommandSource source = context.getSource(); - ServerWorld backrooms = source.getServer().getWorld(Szar.BACKROOMS_KEY); - if (backrooms == null) { - source.sendError(Text.literal("Backrooms dimension not found")); - return 0; - } - BackroomsLightManager.currentEvent = BackroomsLightManager.GlobalEvent.FLICKER; - BackroomsLightManager.eventTimer = 3600; // 3 minutes default - BackroomsLightManager.cooldownTimer = 3600; - source.sendMessage(Text.literal("§eBackrooms lights set to flickering")); - return 1; - }) + .then(CommandManager.literal("set") + .then(CommandManager.literal("normal") + .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.NONE; + BackroomsLightManager.eventTimer = 0; + BackroomsLightManager.cooldownTimer = 3600; + 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(); - ServerWorld backrooms = source.getServer().getWorld(Szar.BACKROOMS_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; - }) + ) + + // /szar drunk + .then(CommandManager.literal("drunk") + .then(CommandManager.argument("targets", + net.minecraft.command.argument.EntityArgumentType.players()) + .then(CommandManager.literal("aggressive") + .executes(ctx -> applyDrunk(ctx, + net.minecraft.command.argument.EntityArgumentType.getPlayers(ctx, "targets"), + DrunkEffect.DrunkType.AGGRESSIVE))) + .then(CommandManager.literal("stumbling") + .executes(ctx -> applyDrunk(ctx, + net.minecraft.command.argument.EntityArgumentType.getPlayers(ctx, "targets"), + 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(); // 🔄 Dimension change ServerEntityWorldChangeEvents.AFTER_PLAYER_CHANGE_WORLD.register((player, origin, destination) -> { - if (origin.getRegistryKey() == Szar.BACKROOMS_KEY) { + if (origin.getRegistryKey() == Szar.BACKROOMS_LEVEL_KEY) { restoreIfNeeded(player); } }); @@ -1202,7 +1221,7 @@ public class Szar implements ModInitializer { ServerPlayConnectionEvents.JOIN.register((handler, sender, server) -> { ServerPlayerEntity player = handler.getPlayer(); - if (player.getWorld().getRegistryKey() != Szar.BACKROOMS_KEY) { + if (player.getWorld().getRegistryKey() != Szar.BACKROOMS_LEVEL_KEY) { restoreIfNeeded(player); } }); @@ -2267,5 +2286,26 @@ public class Szar implements ModInitializer { tag.remove("OwnerTrackerY"); tag.remove("OwnerTrackerZ"); } + + private static int applyDrunk(com.mojang.brigadier.context.CommandContext ctx, + java.util.Collection 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(); + } } diff --git a/src/main/java/dev/tggamesyt/szar/mixin/LevelSummaryMixin.java b/src/main/java/dev/tggamesyt/szar/mixin/LevelSummaryMixin.java new file mode 100644 index 0000000..744934e --- /dev/null +++ b/src/main/java/dev/tggamesyt/szar/mixin/LevelSummaryMixin.java @@ -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 cir) { + cir.setReturnValue(false); + } +} \ No newline at end of file diff --git a/src/main/resources/szar.mixins.json b/src/main/resources/szar.mixins.json index 12cd7b0..6c3080d 100644 --- a/src/main/resources/szar.mixins.json +++ b/src/main/resources/szar.mixins.json @@ -6,6 +6,7 @@ "mixins": [ "CraftingScreenHandlerMixin", "CraftingScreenHandlerMixin2", + "LevelSummaryMixin", "LivingEntityFallDamageMixin", "NoClipMixin", "PlaneBlockInteractionMixin",