ChatDataManager.java 6.05 KB
Newer Older
1
package com.owlmaddie.chat;
2

3 4
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
5
import com.owlmaddie.network.ServerPackets;
6 7 8 9
import net.minecraft.server.MinecraftServer;
import net.minecraft.util.WorldSavePath;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
10

11
import java.io.*;
12
import java.lang.reflect.Type;
13
import java.nio.charset.StandardCharsets;
14
import java.util.HashMap;
15
import java.util.concurrent.ConcurrentHashMap;
16

17 18 19 20
/**
 * The {@code ChatDataManager} class manages chat data for all entities. This class also helps
 * generate new messages, set entity goals, and other useful chat-related functions.
 */
21 22
public class ChatDataManager {
    // Use a static instance to manage our data globally
23 24
    private static final ChatDataManager SERVER_INSTANCE = new ChatDataManager(true);
    private static final ChatDataManager CLIENT_INSTANCE = new ChatDataManager(false);
25
    public static final Logger LOGGER = LoggerFactory.getLogger("creaturechat");
26 27
    public static int MAX_CHAR_PER_LINE = 20;
    public static int DISPLAY_NUM_LINES = 3;
28
    public static int MAX_CHAR_IN_USER_MESSAGE = 512;
29
    public static int TICKS_TO_DISPLAY_USER_MESSAGE = 70;
30
    public static int MAX_AUTOGENERATE_RESPONSES = 3;
31
    private static final Gson GSON = new Gson();
32

33 34 35 36
    public enum ChatStatus {
        NONE,       // No chat status yet
        PENDING,    // Chat is pending (e.g., awaiting response or processing)
        DISPLAY,    // Chat is currently being displayed
37
        HIDDEN,     // Chat is currently hidden
38 39
    }

40 41 42 43 44
    public enum ChatSender {
        USER,      // A user chat message
        ASSISTANT  // A GPT generated message
    }

45
    // HashMap to associate unique entity IDs with their chat data
46
    public ConcurrentHashMap<String, EntityChatData> entityChatDataMap;
47

48 49 50 51 52
    public void clearData() {
        // Clear the chat data for the previous session
        entityChatDataMap.clear();
    }

53 54
    private ChatDataManager(Boolean server_only) {
        // Constructor
55
        entityChatDataMap = new ConcurrentHashMap<>();
56 57 58 59

        if (server_only) {
            // Generate initial quest
            // TODO: Complete the quest flow
60
            //generateQuest();
61
        }
62 63
    }

64 65 66 67 68 69 70 71
    // Method to get the global instance of the server data manager
    public static ChatDataManager getServerInstance() {
        return SERVER_INSTANCE;
    }

    // Method to get the global instance of the client data manager (synced from server)
    public static ChatDataManager getClientInstance() {
        return CLIENT_INSTANCE;
72 73 74
    }

    // Retrieve chat data for a specific entity, or create it if it doesn't exist
75 76
    public EntityChatData getOrCreateChatData(String entityId) {
        return entityChatDataMap.computeIfAbsent(entityId, k -> new EntityChatData(entityId));
77
    }
78

79 80 81 82 83 84 85 86 87
    // Update the UUID in the map (i.e. bucketed entity and then released, changes their UUID)
    public void updateUUID(String oldUUID, String newUUID) {
        EntityChatData data = entityChatDataMap.remove(oldUUID);
        if (data != null) {
            data.entityId = newUUID;
            entityChatDataMap.put(newUUID, data);
            LOGGER.info("Updated chat data from UUID (" + oldUUID + ") to UUID (" + newUUID + ")");

            // Broadcast to all players
88
            ServerPackets.BroadcastEntityMessage(data);
89 90 91 92 93
        } else {
            LOGGER.info("Unable to update chat data, UUID not found: " + oldUUID);
        }
    }

94
    // Save chat data to file
95
    public String GetLightChatData(String playerName) {
96 97
        try {
            // Create "light" version of entire chat data HashMap
98
            HashMap<String, EntityChatDataLight> lightVersionMap = new HashMap<>();
99
            this.entityChatDataMap.forEach((name, entityChatData) -> lightVersionMap.put(name, entityChatData.toLightVersion(playerName)));
100
            return GSON.toJson(lightVersionMap);
101 102 103 104 105 106
        } catch (Exception e) {
            // Handle exceptions
            return "";
        }
    }

107 108
    // Save chat data to file
    public void saveChatData(MinecraftServer server) {
109 110 111
        File saveFile = new File(server.getSavePath(WorldSavePath.ROOT).toFile(), "chatdata.json");
        LOGGER.info("Saving chat data to " + saveFile.getAbsolutePath());

112 113 114
        // Clean up blank, temp entities in data
        entityChatDataMap.values().removeIf(entityChatData -> entityChatData.status == ChatStatus.NONE);

115
        try (Writer writer = new OutputStreamWriter(new FileOutputStream(saveFile), StandardCharsets.UTF_8)) {
116
            GSON.toJson(this.entityChatDataMap, writer);
117
        } catch (Exception e) {
118
            String errorMessage = "Error saving `chatdata.json`. No CreatureChat chat history was saved! " + e.getMessage();
119
            LOGGER.error(errorMessage, e);
120
            ServerPackets.sendErrorToAllOps(server, errorMessage);
121 122 123 124 125
        }
    }

    // Load chat data from file
    public void loadChatData(MinecraftServer server) {
126 127 128 129 130
        File loadFile = new File(server.getSavePath(WorldSavePath.ROOT).toFile(), "chatdata.json");
        LOGGER.info("Loading chat data from " + loadFile.getAbsolutePath());

        if (loadFile.exists()) {
            try (InputStreamReader reader = new InputStreamReader(new FileInputStream(loadFile), StandardCharsets.UTF_8)) {
131
                Type type = new TypeToken<ConcurrentHashMap<String, EntityChatData>>(){}.getType();
132
                this.entityChatDataMap = GSON.fromJson(reader, type);
133

134 135 136
                // Clean up blank, temp entities in data
                entityChatDataMap.values().removeIf(entityChatData -> entityChatData.status == ChatStatus.NONE);

137 138 139 140
                // Post-process each EntityChatData object
                for (EntityChatData entityChatData : entityChatDataMap.values()) {
                    entityChatData.postDeserializeInitialization();
                }
141 142
            } catch (Exception e) {
                LOGGER.error("Error loading chat data", e);
143
                this.entityChatDataMap = new ConcurrentHashMap<>();
144
            }
145 146
        } else {
            // Init empty chat data
147
            this.entityChatDataMap = new ConcurrentHashMap<>();
148 149
        }
    }
150
}