mirror of
https://github.com/ritonioz/minecraft-ai-companion-mod-EMVs12-Project.git
synced 2026-06-20 20:25:01 +02:00
@@ -31,6 +31,13 @@ public class AiChatSession {
|
|||||||
public void sendMessage(String userMessage, Consumer<String> onResponse) {
|
public void sendMessage(String userMessage, Consumer<String> onResponse) {
|
||||||
history.add(new String[]{"user", userMessage});
|
history.add(new String[]{"user", userMessage});
|
||||||
|
|
||||||
|
if (AiCompanionClient.tryTriggerArchEasterEgg(userMessage, response -> {
|
||||||
|
history.add(new String[]{"assistant", response});
|
||||||
|
onResponse.accept(response);
|
||||||
|
})) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
new Thread(() -> {
|
new Thread(() -> {
|
||||||
try {
|
try {
|
||||||
String response = callOpenWebUI();
|
String response = callOpenWebUI();
|
||||||
|
|||||||
@@ -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.networking.v1.ClientPlayNetworking;
|
||||||
import net.fabricmc.fabric.api.client.rendering.v1.EntityRendererRegistry;
|
import net.fabricmc.fabric.api.client.rendering.v1.EntityRendererRegistry;
|
||||||
import net.fabricmc.fabric.api.event.player.UseEntityCallback;
|
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.MinecraftClient;
|
||||||
import net.minecraft.client.render.entity.BipedEntityRenderer;
|
import net.minecraft.client.render.entity.BipedEntityRenderer;
|
||||||
import net.minecraft.client.render.entity.model.EntityModelLayers;
|
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.ActionResult;
|
||||||
import net.minecraft.util.Identifier;
|
import net.minecraft.util.Identifier;
|
||||||
|
|
||||||
|
import java.util.ArrayDeque;
|
||||||
|
import java.util.Deque;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
public class AiCompanionClient implements ClientModInitializer {
|
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
|
// One session per world load, shared across all right-clicks
|
||||||
private static AiChatSession currentSession = null;
|
private static AiChatSession currentSession = null;
|
||||||
|
private static final Deque<Consumer<String>> pendingArchResponses = new ArrayDeque<>();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onInitializeClient() {
|
public void onInitializeClient() {
|
||||||
@@ -31,7 +39,7 @@ public class AiCompanionClient implements ClientModInitializer {
|
|||||||
) {
|
) {
|
||||||
@Override
|
@Override
|
||||||
public Identifier getTexture(AIEntity entity) {
|
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
|
// Open chat GUI on right-click
|
||||||
UseEntityCallback.EVENT.register((player, world, hand, entity, hitResult) -> {
|
UseEntityCallback.EVENT.register((player, world, hand, entity, hitResult) -> {
|
||||||
if (world.isClient() && entity instanceof AIEntity) {
|
if (world.isClient() && entity instanceof AIEntity) {
|
||||||
@@ -89,6 +102,29 @@ public class AiCompanionClient implements ClientModInitializer {
|
|||||||
action.run();
|
action.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean tryTriggerArchEasterEgg(String message, Consumer<String> 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) {
|
private static void askQuestion(MinecraftClient client, String question) {
|
||||||
sendChatMessage(client, Text.literal("§6[AI] §fThink about: " + 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<String> callback = pendingArchResponses.pollFirst();
|
||||||
|
if (callback != null) {
|
||||||
|
callback.accept(response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static void openChatScreen(MinecraftClient client) {
|
private static void openChatScreen(MinecraftClient client) {
|
||||||
if (currentSession == null) {
|
if (currentSession == null) {
|
||||||
currentSession = new AiChatSession(
|
currentSession = new AiChatSession(
|
||||||
|
|||||||
@@ -5,6 +5,9 @@ import net.minecraft.entity.ai.goal.*;
|
|||||||
import net.minecraft.entity.ai.pathing.MobNavigation;
|
import net.minecraft.entity.ai.pathing.MobNavigation;
|
||||||
import net.minecraft.entity.attribute.DefaultAttributeContainer;
|
import net.minecraft.entity.attribute.DefaultAttributeContainer;
|
||||||
import net.minecraft.entity.attribute.EntityAttributes;
|
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.damage.DamageSource;
|
||||||
import net.minecraft.entity.passive.PassiveEntity;
|
import net.minecraft.entity.passive.PassiveEntity;
|
||||||
import net.minecraft.entity.passive.TameableEntity;
|
import net.minecraft.entity.passive.TameableEntity;
|
||||||
@@ -17,6 +20,9 @@ import net.minecraft.world.World;
|
|||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
public class AIEntity extends TameableEntity {
|
public class AIEntity extends TameableEntity {
|
||||||
|
private static final TrackedData<Boolean> TUX_MODE =
|
||||||
|
DataTracker.registerData(AIEntity.class, TrackedDataHandlerRegistry.BOOLEAN);
|
||||||
|
private int tuxTicksRemaining = 0;
|
||||||
|
|
||||||
public AIEntity(EntityType<? extends TameableEntity> entityType, World world) {
|
public AIEntity(EntityType<? extends TameableEntity> entityType, World world) {
|
||||||
super(entityType, world);
|
super(entityType, world);
|
||||||
@@ -34,6 +40,12 @@ public class AIEntity extends TameableEntity {
|
|||||||
.add(EntityAttributes.GENERIC_FOLLOW_RANGE, 32.0);
|
.add(EntityAttributes.GENERIC_FOLLOW_RANGE, 32.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void initDataTracker() {
|
||||||
|
super.initDataTracker();
|
||||||
|
this.dataTracker.startTracking(TUX_MODE, false);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void initGoals() {
|
protected void initGoals() {
|
||||||
this.goalSelector.add(1, new FollowOwnerGoal(this, 1.0, 5.0f, 2.0f, false));
|
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));
|
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
|
@Override
|
||||||
public boolean damage(DamageSource source, float amount) {
|
public boolean damage(DamageSource source, float amount) {
|
||||||
// Allow kill command through, block everything else
|
// Allow kill command through, block everything else
|
||||||
@@ -75,6 +100,15 @@ public class AIEntity extends TameableEntity {
|
|||||||
return null;
|
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
|
// Bridge method fix
|
||||||
public EntityView method_48926() {
|
public EntityView method_48926() {
|
||||||
return (EntityView) this.getWorld();
|
return (EntityView) this.getWorld();
|
||||||
|
|||||||
@@ -9,7 +9,10 @@ import java.net.HttpURLConnection;
|
|||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
import com.mojang.brigadier.arguments.StringArgumentType;
|
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.EntityDimensions;
|
||||||
import net.minecraft.entity.EntityType;
|
import net.minecraft.entity.EntityType;
|
||||||
import net.minecraft.entity.SpawnGroup;
|
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.Registries;
|
||||||
import net.minecraft.registry.Registry;
|
import net.minecraft.registry.Registry;
|
||||||
import net.minecraft.server.command.CommandManager;
|
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.text.Text;
|
||||||
import net.minecraft.util.Identifier;
|
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 String API_PATH = DEFAULT_API_PATH;
|
||||||
private static final Path SHARED_CONFIG_PATH = Path.of("config", "aicompanion2_0.properties");
|
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 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<UUID, Long> ARCH_EASTER_EGG_LAST_USED = new HashMap<>();
|
||||||
public static final Identifier QUESTION_PACKET_ID = new Identifier(MOD_ID, "question");
|
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 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<AIEntity> AI_COMPANION = Registry.register(
|
public static final EntityType<AIEntity> AI_COMPANION = Registry.register(
|
||||||
Registries.ENTITY_TYPE,
|
Registries.ENTITY_TYPE,
|
||||||
@@ -61,6 +74,9 @@ public class Aicompanion2_0 implements ModInitializer {
|
|||||||
System.out.println("[" + MOD_ID + "] MOD STARTET!");
|
System.out.println("[" + MOD_ID + "] MOD STARTET!");
|
||||||
|
|
||||||
FabricDefaultAttributeRegistry.register(AI_COMPANION, AIEntity.createMobAttributes());
|
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) -> {
|
CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> {
|
||||||
System.out.println("[" + MOD_ID + "] Registriere /ai command");
|
System.out.println("[" + MOD_ID + "] Registriere /ai command");
|
||||||
@@ -350,4 +366,49 @@ public class Aicompanion2_0 implements ModInitializer {
|
|||||||
}
|
}
|
||||||
return true;
|
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";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 3.1 KiB |
Reference in New Issue
Block a user