RuneHive-Game
Loading...
Searching...
No Matches
LazyAIManager.java
Go to the documentation of this file.
1package com.runehive.content.ai;
2
3import com.runehive.content.dialogue.Expression;
4import com.runehive.game.world.entity.mob.npc.Npc;
5import com.runehive.game.world.entity.mob.player.Player;
6import com.runehive.game.world.position.Position;
7import org.slf4j.Logger;
8import org.slf4j.LoggerFactory;
9
10import com.runehive.game.task.Task;
11import com.runehive.game.world.World;
12import java.util.concurrent.atomic.AtomicBoolean;
13import java.util.Set;
14import java.util.Map;
15import java.util.concurrent.ConcurrentHashMap;
16
17public final class LazyAIManager {
18
19 private static final Logger logger = LoggerFactory.getLogger(LazyAIManager.class);
20 private static final AtomicBoolean openAIInitialized = new AtomicBoolean(false);
21 private static final int GANDALF_AI_ID = 2108;
22
23 private static final Set<String> sessionConsents = ConcurrentHashMap.newKeySet();
24 private static final Map<String, Task> inactivityTimers = new ConcurrentHashMap<>();
25
26 private static volatile OpenAIService openAIService;
27
28 public static boolean handleGandalfAIClick(Player player, Npc npc) {
29 if (npc.id != GANDALF_AI_ID) {
30 return false;
31 }
32
33 // Position player 1 tile west of NPC and setup camera
34 Position npcPos = npc.getPosition();
35 Position playerTarget = new Position(npcPos.getX() - 1, npcPos.getY(), npcPos.getHeight());
36 player.move(playerTarget);
37 player.face(npc);
38
39 // Initialize dialogue camera - player speaks first
40 player.dialogueCamNpc = npc;
41 player.dialogueCamMode = com.runehive.game.world.entity.mob.player.camera.DialogueCameraDirector.Mode.BEHIND_PLAYER;
42
43 if (sessionConsents.contains(player.getUsername())) {
44 if (!openAIInitialized.get()) {
46 }
47
48 // Always show instructions when NPC is clicked (connection is idle)
49 showGptInstructions(player, npc);
50 } else {
51 showOpenAIConnectionOffer(player, npc);
52 }
53 return true;
54 }
55
56 private static void showOpenAIConnectionOffer(Player player, Npc npc) {
57 player.dialogueFactory
58 .sendPlayerChat("Hey, Wise Old Man!")
59 .onAction(() -> player.dialogueCamMode = com.runehive.game.world.entity.mob.player.camera.DialogueCameraDirector.Mode.BEHIND_NPC)
60 .sendNpcChat(npc.id, Expression.HAPPY,
61 "Greetings, " + player.getUsername() + "!",
62 "I am Gandalf GPT4o. I can connect to the OpenAI",
63 "mystical networks to provide you with advanced",
64 "AI assistance.")
65 .sendNpcChat(npc.id, Expression.HAPPY,
66 "Would you like me to establish this connection?")
67 .sendOption(
68 "Yes, connect to OpenAI", () -> {
69 sessionConsents.add(player.getUsername());
70 logger.info("Player {} consented to OpenAI connection for this session", player.getUsername());
71
72 // Send blue chat message to player about service initialization
73 String timestamp = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new java.util.Date());
74 player.message("<col=0000FF>[OpenAI Service] Initialized at " + timestamp + ". Conversation History = 10.</col>");
75
76 initializeOpenAIAndProcess(player, npc);
77 },
78 "No, maybe later", () -> {
79 player.dialogueFactory
80 .sendNpcChat(npc.id, Expression.HAPPY,
81 "Very well. I shall remain here if you change",
82 "your mind. The mystical networks await when",
83 "you are ready.")
84 .execute();
85 }
86 )
87 .execute();
88 }
89
90 private static void initializeOpenAIAndProcess(Player player, Npc npc) {
91 if (!openAIInitialized.get()) {
94 } else {
95 player.dialogueFactory
96 .sendNpcChat(npc.id, Expression.SAD,
97 "Alas! The mystical networks are unreachable.",
98 "Please ensure the OpenAI key scroll is present",
99 "and try again later.")
100 .execute();
101 return;
102 }
103 } else {
105 }
106 }
107
108 private static synchronized boolean initializeOpenAIServices() {
109 if (openAIInitialized.compareAndSet(false, true)) {
110 logger.info("Player accepted OpenAI connection - initializing OpenAI services...");
111
112 try {
114 openAIService.initialize();
115 logger.info("Official OpenAI Service initialized with GPT-4o-mini model");
116
117 logger.info("OpenAI services fully initialized and ready for interactions!");
118 return true;
119
120 } catch (Exception e) {
121 logger.error("Failed to initialize OpenAI services", e);
122 openAIInitialized.set(false);
123 return false;
124 }
125 }
126 return true;
127 }
128
129 public static boolean isInitialized() {
130 return openAIInitialized.get();
131 }
132
134 return openAIService;
135 }
136
137 private static void showGptInstructionsWithGreeting(Player player, Npc npc) {
138 npc.animate(new com.runehive.game.Animation(858));
139 npc.speak("GPT 4o at your service!");
140 player.dialogueFactory
141 .sendPlayerChat("Hello again, Wise Old Man!")
142 .onAction(() -> player.dialogueCamMode = com.runehive.game.world.entity.mob.player.camera.DialogueCameraDirector.Mode.BEHIND_NPC)
143 .sendNpcChat(npc.id, Expression.HAPPY,
144 "To send a message, type:",
145 "<col=0000FF>::gpt <your message></col>")
146 .onAction(() -> {
147 player.interfaceManager.close();
148 com.runehive.game.world.entity.mob.player.camera.DialogueCameraDirector.requestReset(player);
149 startInactivityTimer(player, npc);
150 })
151 .execute();
152 }
153
154 private static void showGptInstructions(Player player, Npc npc) {
155 player.dialogueFactory
156 .sendPlayerChat("Hello again, Wise Old Man!")
157 .onAction(() -> player.dialogueCamMode = com.runehive.game.world.entity.mob.player.camera.DialogueCameraDirector.Mode.BEHIND_NPC)
158 .sendNpcChat(npc.id, Expression.HAPPY,
159 "To send a message, type:",
160 "<col=0000FF>::gpt <your message></col>")
161 .onAction(() -> {
162 player.interfaceManager.close();
163 com.runehive.game.world.entity.mob.player.camera.DialogueCameraDirector.requestReset(player);
164 })
165 .execute();
166 }
167
168 public static void markInstructionsReceived(String username) {
169 cancelInactivityTimer(username);
170 }
171
172 private static void startInactivityTimer(Player player, Npc npc) {
174
175 Task timer = new Task(100) {
176 int warningsSent = 0;
177
178 @Override
179 public void execute() {
180 if (!player.isRegistered() || !sessionConsents.contains(player.getUsername())) {
181 cancel();
182 return;
183 }
184
185 if (warningsSent == 0) {
186 player.dialogueFactory
187 .sendNpcChat(npc.id, Expression.HAPPY,
188 "Hey " + player.getUsername() + ", I'm still here!",
189 "If I don't hear back from you within 1 minute,",
190 "I shall close the OpenAI realms. Your message",
191 "history would be reset in this event.")
192 .execute();
193 warningsSent++;
194 } else {
195 player.dialogueFactory
196 .sendNpcChat(npc.id, Expression.HAPPY,
197 "Farewell, " + player.getUsername() + "!")
198 .execute();
200 cancel();
201 }
202 }
203 };
204
205 World.schedule(timer);
206 inactivityTimers.put(player.getUsername(), timer);
207 }
208
209 private static void cancelInactivityTimer(String username) {
210 Task timer = inactivityTimers.remove(username);
211 if (timer != null) {
212 timer.cancel();
213 }
214 }
215
216 public static void clearPlayerConsent(String username) {
217 if (sessionConsents.remove(username)) {
218 logger.info("Cleared OpenAI consent for player: {}", username);
219
220 // Send blue chat message to player about service termination
221 World.getPlayerByName(username).ifPresent(player -> {
222 String timestamp = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new java.util.Date());
223 player.message("<col=0000FF>[OpenAI Service] Terminated at " + timestamp + ". Conversation History flushed.</col>");
224 });
225 }
226
227 cancelInactivityTimer(username);
228
229 if (openAIService != null) {
230 openAIService.clearConversationHistory(username);
231 }
232 }
233
234 public static void shutdown() {
235 if (openAIInitialized.get() && openAIService != null) {
236 logger.info("Shutting down OpenAI services...");
237 openAIService.shutdown();
238 }
239 sessionConsents.clear();
240 inactivityTimers.values().forEach(Task::cancel);
241 inactivityTimers.clear();
242 }
243}
static void showGptInstructions(Player player, Npc npc)
static void initializeOpenAIAndProcess(Player player, Npc npc)
static final Map< String, Task > inactivityTimers
static void clearPlayerConsent(String username)
static volatile OpenAIService openAIService
static void markInstructionsReceived(String username)
static void showOpenAIConnectionOffer(Player player, Npc npc)
static void showGptInstructionsWithGreeting(Player player, Npc npc)
static final AtomicBoolean openAIInitialized
static synchronized boolean initializeOpenAIServices()
static final Set< String > sessionConsents
static boolean handleGandalfAIClick(Player player, Npc npc)
static void startInactivityTimer(Player player, Npc npc)
static void cancelInactivityTimer(String username)
A game representing a cyclic unit of work.
Definition Task.java:11
synchronized final void cancel()
Cancels all subsequent executions.
Definition Task.java:113
Represents the game world.
Definition World.java:46
static void schedule(Task task)
Submits a new event.
Definition World.java:247
static Optional< Player > getPlayerByName(String username)
Definition World.java:191
void speak(String forceChat)
Sets the mob's forced chat.
Definition Mob.java:127
void move(Position position)
Moves the mob to a set position.
Definition Mob.java:340
void face(GameObject object)
Sets the client update flag to face a certain direction.
Definition Mob.java:289
Represents a non-player character in the in-game world.
Definition Npc.java:29
This class represents a character controlled by a player.
Definition Player.java:125
transient com.runehive.game.world.entity.mob.player.camera.DialogueCameraDirector.Mode dialogueCamMode
Dialogue camera system.
Definition Player.java:169
Represents a single tile on the game world.
Definition Position.java:14
int getHeight()
Gets the height coordinate, or height.
Definition Position.java:51
int getY()
Gets the absolute y coordinate.
Definition Position.java:46
int getX()
Gets the absolute x coordinate.
Definition Position.java:41
Represents the expressions of entities for dialogue.