From 8cc6fa53d9b39a2d07b2dd4769724e9870448a76 Mon Sep 17 00:00:00 2001 From: Cametendo Date: Wed, 18 Mar 2026 11:11:14 +0100 Subject: [PATCH] Added easter egg --- .../aicompanion2_0/client/AiChatSession.java | 7 ++ .../client/AiCompanionClient.java | 52 ++++++++++++++- .../AiCompanion/aicompanion2_0/AIEntity.java | 36 ++++++++++- .../aicompanion2_0/Aicompanion2_0.java | 61 ++++++++++++++++++ .../aicompanion2_0/textures/entity/tux-mc.png | Bin 0 -> 3188 bytes 5 files changed, 154 insertions(+), 2 deletions(-) create mode 100644 src/main/resources/assets/aicompanion2_0/textures/entity/tux-mc.png diff --git a/src/client/java/AiCompanion/aicompanion2_0/client/AiChatSession.java b/src/client/java/AiCompanion/aicompanion2_0/client/AiChatSession.java index 8df558a..b244660 100644 --- a/src/client/java/AiCompanion/aicompanion2_0/client/AiChatSession.java +++ b/src/client/java/AiCompanion/aicompanion2_0/client/AiChatSession.java @@ -31,6 +31,13 @@ public class AiChatSession { public void sendMessage(String userMessage, Consumer onResponse) { history.add(new String[]{"user", userMessage}); + if (AiCompanionClient.tryTriggerArchEasterEgg(userMessage, response -> { + history.add(new String[]{"assistant", response}); + onResponse.accept(response); + })) { + return; + } + new Thread(() -> { try { String response = callOpenWebUI(); diff --git a/src/client/java/AiCompanion/aicompanion2_0/client/AiCompanionClient.java b/src/client/java/AiCompanion/aicompanion2_0/client/AiCompanionClient.java index 398303c..f4e8666 100644 --- a/src/client/java/AiCompanion/aicompanion2_0/client/AiCompanionClient.java +++ b/src/client/java/AiCompanion/aicompanion2_0/client/AiCompanionClient.java @@ -7,6 +7,7 @@ import net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents; import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking; import net.fabricmc.fabric.api.client.rendering.v1.EntityRendererRegistry; import net.fabricmc.fabric.api.event.player.UseEntityCallback; +import net.fabricmc.fabric.api.networking.v1.PacketByteBufs; import net.minecraft.client.MinecraftClient; import net.minecraft.client.render.entity.BipedEntityRenderer; import net.minecraft.client.render.entity.model.EntityModelLayers; @@ -15,10 +16,17 @@ import net.minecraft.text.Text; import net.minecraft.util.ActionResult; import net.minecraft.util.Identifier; +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.function.Consumer; + public class AiCompanionClient implements ClientModInitializer { + private static final Identifier DEFAULT_TEXTURE = new Identifier("aicompanion2_0", "textures/entity/skin.png"); + private static final Identifier TUX_TEXTURE = new Identifier("aicompanion2_0", "textures/entity/tux-mc.png"); // One session per world load, shared across all right-clicks private static AiChatSession currentSession = null; + private static final Deque> pendingArchResponses = new ArrayDeque<>(); @Override public void onInitializeClient() { @@ -31,7 +39,7 @@ public class AiCompanionClient implements ClientModInitializer { ) { @Override public Identifier getTexture(AIEntity entity) { - return new Identifier("aicompanion2_0", "textures/entity/skin.png"); + return entity.isTuxMode() ? TUX_TEXTURE : DEFAULT_TEXTURE; } } ); @@ -64,6 +72,11 @@ public class AiCompanionClient implements ClientModInitializer { }); }); + ClientPlayNetworking.registerGlobalReceiver(Aicompanion2_0.ARCH_EASTER_EGG_RESPONSE_PACKET_ID, (client, handler, buf, responseSender) -> { + String response = buf.readString(32767); + client.execute(() -> finishArchEasterEgg(response)); + }); + // Open chat GUI on right-click UseEntityCallback.EVENT.register((player, world, hand, entity, hitResult) -> { if (world.isClient() && entity instanceof AIEntity) { @@ -89,6 +102,29 @@ public class AiCompanionClient implements ClientModInitializer { action.run(); } + public static boolean tryTriggerArchEasterEgg(String message, Consumer onResponse) { + if (!isArchEasterEggTrigger(message)) { + return false; + } + + if (!ClientPlayNetworking.canSend(Aicompanion2_0.ARCH_EASTER_EGG_PACKET_ID)) { + onResponse.accept("§cError: The server does not support the Arch easter egg."); + return true; + } + + pendingArchResponses.addLast(onResponse); + + try { + ClientPlayNetworking.send(Aicompanion2_0.ARCH_EASTER_EGG_PACKET_ID, PacketByteBufs.create()); + } catch (RuntimeException e) { + pendingArchResponses.pollLast(); + String error = e.getMessage() != null ? e.getMessage() : e.getClass().getSimpleName(); + onResponse.accept("§cError: " + error); + } + + return true; + } + private static void askQuestion(MinecraftClient client, String question) { sendChatMessage(client, Text.literal("§6[AI] §fThink about: " + question)); @@ -124,6 +160,20 @@ public class AiCompanionClient implements ClientModInitializer { } } + private static boolean isArchEasterEggTrigger(String message) { + String normalized = message.trim().replaceAll("\\s+", " ").toLowerCase(); + return "i use arch btw".equals(normalized) + || "sudo".equals(normalized) + || normalized.startsWith("sudo "); + } + + private static void finishArchEasterEgg(String response) { + Consumer callback = pendingArchResponses.pollFirst(); + if (callback != null) { + callback.accept(response); + } + } + private static void openChatScreen(MinecraftClient client) { if (currentSession == null) { currentSession = new AiChatSession( diff --git a/src/main/java/AiCompanion/aicompanion2_0/AIEntity.java b/src/main/java/AiCompanion/aicompanion2_0/AIEntity.java index 6bb63ca..2a9c10d 100644 --- a/src/main/java/AiCompanion/aicompanion2_0/AIEntity.java +++ b/src/main/java/AiCompanion/aicompanion2_0/AIEntity.java @@ -5,6 +5,9 @@ import net.minecraft.entity.ai.goal.*; import net.minecraft.entity.ai.pathing.MobNavigation; import net.minecraft.entity.attribute.DefaultAttributeContainer; import net.minecraft.entity.attribute.EntityAttributes; +import net.minecraft.entity.data.DataTracker; +import net.minecraft.entity.data.TrackedData; +import net.minecraft.entity.data.TrackedDataHandlerRegistry; import net.minecraft.entity.damage.DamageSource; import net.minecraft.entity.passive.PassiveEntity; import net.minecraft.entity.passive.TameableEntity; @@ -17,6 +20,9 @@ import net.minecraft.world.World; import org.jetbrains.annotations.Nullable; public class AIEntity extends TameableEntity { + private static final TrackedData TUX_MODE = + DataTracker.registerData(AIEntity.class, TrackedDataHandlerRegistry.BOOLEAN); + private int tuxTicksRemaining = 0; public AIEntity(EntityType entityType, World world) { super(entityType, world); @@ -34,6 +40,12 @@ public class AIEntity extends TameableEntity { .add(EntityAttributes.GENERIC_FOLLOW_RANGE, 32.0); } + @Override + protected void initDataTracker() { + super.initDataTracker(); + this.dataTracker.startTracking(TUX_MODE, false); + } + @Override protected void initGoals() { this.goalSelector.add(1, new FollowOwnerGoal(this, 1.0, 5.0f, 2.0f, false)); @@ -42,6 +54,19 @@ public class AIEntity extends TameableEntity { this.goalSelector.add(4, new LookAroundGoal(this)); } + @Override + public void tick() { + super.tick(); + + if (!this.getWorld().isClient() && tuxTicksRemaining > 0) { + tuxTicksRemaining--; + + if (tuxTicksRemaining == 0) { + this.dataTracker.set(TUX_MODE, false); + } + } + } + @Override public boolean damage(DamageSource source, float amount) { // Allow kill command through, block everything else @@ -75,8 +100,17 @@ public class AIEntity extends TameableEntity { return null; } + public void activateTuxMode(int durationTicks) { + tuxTicksRemaining = Math.max(tuxTicksRemaining, durationTicks); + this.dataTracker.set(TUX_MODE, true); + } + + public boolean isTuxMode() { + return this.dataTracker.get(TUX_MODE); + } + // Bridge method fix public EntityView method_48926() { return (EntityView) this.getWorld(); } -} \ No newline at end of file +} diff --git a/src/main/java/AiCompanion/aicompanion2_0/Aicompanion2_0.java b/src/main/java/AiCompanion/aicompanion2_0/Aicompanion2_0.java index 45c5aaf..c5fda63 100644 --- a/src/main/java/AiCompanion/aicompanion2_0/Aicompanion2_0.java +++ b/src/main/java/AiCompanion/aicompanion2_0/Aicompanion2_0.java @@ -9,7 +9,10 @@ import java.net.HttpURLConnection; import java.net.URL; import java.nio.file.Path; import java.nio.file.Files; +import java.util.HashMap; +import java.util.Map; import java.util.Properties; +import java.util.UUID; import com.mojang.brigadier.arguments.StringArgumentType; @@ -22,9 +25,13 @@ import net.fabricmc.fabric.api.object.builder.v1.entity.FabricEntityTypeBuilder; import net.minecraft.entity.EntityDimensions; import net.minecraft.entity.EntityType; import net.minecraft.entity.SpawnGroup; +import net.minecraft.entity.effect.StatusEffectInstance; +import net.minecraft.entity.effect.StatusEffects; import net.minecraft.registry.Registries; import net.minecraft.registry.Registry; import net.minecraft.server.command.CommandManager; +import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.server.world.ServerWorld; import net.minecraft.text.Text; import net.minecraft.util.Identifier; @@ -40,8 +47,14 @@ public class Aicompanion2_0 implements ModInitializer { private static String API_PATH = DEFAULT_API_PATH; private static final Path SHARED_CONFIG_PATH = Path.of("config", "aicompanion2_0.properties"); private static final Path CLIENT_CONFIG_PATH = Path.of("config", "aicompanion2_0_client.properties"); + private static final long ARCH_EASTER_EGG_COOLDOWN_MS = 15L * 60L * 1000L; + private static final int ARCH_EASTER_EGG_BUFF_DURATION_TICKS = 20 * 60; + private static final int ARCH_EASTER_EGG_TUX_DURATION_TICKS = 20 * 10; + private static final Map ARCH_EASTER_EGG_LAST_USED = new HashMap<>(); public static final Identifier QUESTION_PACKET_ID = new Identifier(MOD_ID, "question"); public static final Identifier DELETE_KEY_PACKET_ID = new Identifier(MOD_ID, "delete_key"); + public static final Identifier ARCH_EASTER_EGG_PACKET_ID = new Identifier(MOD_ID, "arch_easter_egg"); + public static final Identifier ARCH_EASTER_EGG_RESPONSE_PACKET_ID = new Identifier(MOD_ID, "arch_easter_egg_response"); public static final EntityType AI_COMPANION = Registry.register( Registries.ENTITY_TYPE, @@ -61,6 +74,9 @@ public class Aicompanion2_0 implements ModInitializer { System.out.println("[" + MOD_ID + "] MOD STARTET!"); FabricDefaultAttributeRegistry.register(AI_COMPANION, AIEntity.createMobAttributes()); + ServerPlayNetworking.registerGlobalReceiver(ARCH_EASTER_EGG_PACKET_ID, (server, player, handler, buf, responseSender) -> + server.execute(() -> sendArchEasterEggResponse(player)) + ); CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> { System.out.println("[" + MOD_ID + "] Registriere /ai command"); @@ -350,4 +366,49 @@ public class Aicompanion2_0 implements ModInitializer { } return true; } + + private void sendArchEasterEggResponse(ServerPlayerEntity player) { + if (!ServerPlayNetworking.canSend(player, ARCH_EASTER_EGG_RESPONSE_PACKET_ID)) { + return; + } + + String response = triggerArchEasterEgg(player); + var buf = PacketByteBufs.create(); + buf.writeString(response); + ServerPlayNetworking.send(player, ARCH_EASTER_EGG_RESPONSE_PACKET_ID, buf); + } + + private String triggerArchEasterEgg(ServerPlayerEntity player) { + long now = System.currentTimeMillis(); + Long lastUsed = ARCH_EASTER_EGG_LAST_USED.get(player.getUuid()); + + if (lastUsed != null) { + long remainingMs = ARCH_EASTER_EGG_COOLDOWN_MS - (now - lastUsed); + if (remainingMs > 0) { + return "Optimization already complete. Cooldown remaining: " + formatCooldown(remainingMs) + "."; + } + } + + ARCH_EASTER_EGG_LAST_USED.put(player.getUuid(), now); + player.addStatusEffect(new StatusEffectInstance(StatusEffects.SPEED, ARCH_EASTER_EGG_BUFF_DURATION_TICKS, 1)); + player.addStatusEffect(new StatusEffectInstance(StatusEffects.HASTE, ARCH_EASTER_EGG_BUFF_DURATION_TICKS, 1)); + activateTuxModeForOwnedCompanions(player); + return "Optimization complete. Bloatware removed."; + } + + private void activateTuxModeForOwnedCompanions(ServerPlayerEntity player) { + for (ServerWorld world : player.getServer().getWorlds()) { + for (AIEntity companion : world.getEntitiesByType(AI_COMPANION, entity -> + player.getUuid().equals(entity.getOwnerUuid()))) { + companion.activateTuxMode(ARCH_EASTER_EGG_TUX_DURATION_TICKS); + } + } + } + + private String formatCooldown(long remainingMs) { + long totalSeconds = Math.max(1, (remainingMs + 999) / 1000); + long minutes = totalSeconds / 60; + long seconds = totalSeconds % 60; + return minutes + "m " + seconds + "s"; + } } diff --git a/src/main/resources/assets/aicompanion2_0/textures/entity/tux-mc.png b/src/main/resources/assets/aicompanion2_0/textures/entity/tux-mc.png new file mode 100644 index 0000000000000000000000000000000000000000..3e9e99378a1f219a6e368248c681946852d79d15 GIT binary patch literal 3188 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