first commit
This commit is contained in:
commit
2f05711bb4
8
.idea/.gitignore
vendored
Normal file
8
.idea/.gitignore
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
15
.idea/checkstyle-idea.xml
Normal file
15
.idea/checkstyle-idea.xml
Normal file
@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CheckStyle-IDEA" serialisationVersion="2">
|
||||
<checkstyleVersion>10.20.1</checkstyleVersion>
|
||||
<scanScope>JavaOnly</scanScope>
|
||||
<option name="thirdPartyClasspath" />
|
||||
<option name="activeLocationIds" />
|
||||
<option name="locations">
|
||||
<list>
|
||||
<ConfigurationLocation id="bundled-sun-checks" type="BUNDLED" scope="All" description="Sun Checks">(bundled)</ConfigurationLocation>
|
||||
<ConfigurationLocation id="bundled-google-checks" type="BUNDLED" scope="All" description="Google Checks">(bundled)</ConfigurationLocation>
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
13
.idea/compiler.xml
Normal file
13
.idea/compiler.xml
Normal file
@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CompilerConfiguration">
|
||||
<annotationProcessing>
|
||||
<profile name="Maven default annotation processors profile" enabled="true">
|
||||
<sourceOutputDir name="target/generated-sources/annotations" />
|
||||
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
|
||||
<outputRelativeToContentRoot value="true" />
|
||||
<module name="npcconversations" />
|
||||
</profile>
|
||||
</annotationProcessing>
|
||||
</component>
|
||||
</project>
|
7
.idea/encodings.xml
Normal file
7
.idea/encodings.xml
Normal file
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Encoding">
|
||||
<file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
|
||||
<file url="file://$PROJECT_DIR$/src/main/resources" charset="UTF-8" />
|
||||
</component>
|
||||
</project>
|
30
.idea/jarRepositories.xml
Normal file
30
.idea/jarRepositories.xml
Normal file
@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="RemoteRepositoriesConfiguration">
|
||||
<remote-repository>
|
||||
<option name="id" value="central" />
|
||||
<option name="name" value="Central Repository" />
|
||||
<option name="url" value="https://repo.maven.apache.org/maven2" />
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="sonatype" />
|
||||
<option name="name" value="sonatype" />
|
||||
<option name="url" value="https://oss.sonatype.org/content/groups/public/" />
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="papermc-repo" />
|
||||
<option name="name" value="papermc-repo" />
|
||||
<option name="url" value="https://repo.papermc.io/repository/maven-public/" />
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="central" />
|
||||
<option name="name" value="Maven Central repository" />
|
||||
<option name="url" value="https://repo1.maven.org/maven2" />
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="jboss.community" />
|
||||
<option name="name" value="JBoss Community repository" />
|
||||
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
|
||||
</remote-repository>
|
||||
</component>
|
||||
</project>
|
14
.idea/misc.xml
Normal file
14
.idea/misc.xml
Normal file
@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||
<component name="MavenProjectsManager">
|
||||
<option name="originalFiles">
|
||||
<list>
|
||||
<option value="$PROJECT_DIR$/pom.xml" />
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="21" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/out" />
|
||||
</component>
|
||||
</project>
|
8
.idea/modules.xml
Normal file
8
.idea/modules.xml
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/npcconversations.iml" filepath="$PROJECT_DIR$/npcconversations.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
6
.idea/vcs.xml
Normal file
6
.idea/vcs.xml
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
67
dependency-reduced-pom.xml
Normal file
67
dependency-reduced-pom.xml
Normal file
@ -0,0 +1,67 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>net.voltexstudios</groupId>
|
||||
<artifactId>npcconversations</artifactId>
|
||||
<name>npcconversations</name>
|
||||
<version>1.0</version>
|
||||
<build>
|
||||
<defaultGoal>clean package</defaultGoal>
|
||||
<resources>
|
||||
<resource>
|
||||
<filtering>true</filtering>
|
||||
<directory>src/main/resources</directory>
|
||||
</resource>
|
||||
</resources>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.13.0</version>
|
||||
<configuration>
|
||||
<source>${java.version}</source>
|
||||
<target>${java.version}</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.5.3</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>papermc-repo</id>
|
||||
<url>https://repo.papermc.io/repository/maven-public/</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>sonatype</id>
|
||||
<url>https://oss.sonatype.org/content/groups/public/</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>io.papermc.paper</groupId>
|
||||
<artifactId>paper-api</artifactId>
|
||||
<version>1.21.3-R0.1-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>1.18.30</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<properties>
|
||||
<java.version>21</java.version>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
</project>
|
13
npcconversations.iml
Normal file
13
npcconversations.iml
Normal file
@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module version="4">
|
||||
<component name="FacetManager">
|
||||
<facet type="minecraft" name="Minecraft">
|
||||
<configuration>
|
||||
<autoDetectTypes>
|
||||
<platformType>ADVENTURE</platformType>
|
||||
</autoDetectTypes>
|
||||
<projectReimportVersion>1</projectReimportVersion>
|
||||
</configuration>
|
||||
</facet>
|
||||
</component>
|
||||
</module>
|
85
pom.xml
Normal file
85
pom.xml
Normal file
@ -0,0 +1,85 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>net.voltexstudios</groupId>
|
||||
<artifactId>npcconversations</artifactId>
|
||||
<version>1.0</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>npcconversations</name>
|
||||
|
||||
<properties>
|
||||
<java.version>21</java.version>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
<defaultGoal>clean package</defaultGoal>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.13.0</version>
|
||||
<configuration>
|
||||
<source>${java.version}</source>
|
||||
<target>${java.version}</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.5.3</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<filtering>true</filtering>
|
||||
</resource>
|
||||
</resources>
|
||||
</build>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>papermc-repo</id>
|
||||
<url>https://repo.papermc.io/repository/maven-public/</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>sonatype</id>
|
||||
<url>https://oss.sonatype.org/content/groups/public/</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>io.papermc.paper</groupId>
|
||||
<artifactId>paper-api</artifactId>
|
||||
<version>1.21.3-R0.1-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<!-- https://mvnrepository.com/artifact/com.launchableinc.openai-java/service -->
|
||||
<dependency>
|
||||
<groupId>com.launchableinc.openai-java</groupId>
|
||||
<artifactId>service</artifactId>
|
||||
<version>0.4.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>1.18.30</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
@ -0,0 +1,109 @@
|
||||
package net.voltexstudios.npcConversations;
|
||||
|
||||
import com.google.common.cache.Cache;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.cache.RemovalCause;
|
||||
import com.google.common.cache.RemovalListener;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.TextComponent;
|
||||
import net.voltexstudios.npcConversations.commands.ConversationCommand;
|
||||
import net.voltexstudios.npcConversations.listener.ChatHandler;
|
||||
import net.voltexstudios.npcConversations.listener.PlayerListener;
|
||||
import net.voltexstudios.npcConversations.npc.NPC;
|
||||
import net.voltexstudios.npcConversations.npc.NPCData;
|
||||
import net.voltexstudios.npcConversations.npc.NPCService;
|
||||
import net.voltexstudios.npcConversations.session.ConverstionSession;
|
||||
import net.voltexstudios.npcConversations.util.OpenAI;
|
||||
import net.voltexstudios.npcConversations.util.Type;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.PluginManager;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.logging.Level;
|
||||
|
||||
public final class NPCConversations extends JavaPlugin {
|
||||
|
||||
public static final TextComponent PREFIX = Component.text("§8[§eConversation§8] §7");
|
||||
|
||||
public static Cache<Player, ConverstionSession> CACHE;
|
||||
public static Cache<Player, Type> USER_TYPE = CacheBuilder.newBuilder().build();
|
||||
|
||||
private static NPCConversations instance;
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
instance = this;
|
||||
|
||||
saveDefaultConfig();
|
||||
|
||||
// Check api key and initialize OpenAI service
|
||||
OpenAI.init(getConfig().getString("API-KEY")).exceptionallyAsync(throwable -> {
|
||||
getLogger().severe("Error while initializing OpenAI service! Is your API key valid?");
|
||||
|
||||
getServer().getPluginManager().disablePlugin(this);
|
||||
return null;
|
||||
});
|
||||
|
||||
// Initialize cache
|
||||
CACHE = CacheBuilder.newBuilder()
|
||||
.expireAfterWrite(getConfig().getInt("conversation-expiration-duration"), TimeUnit.MINUTES)
|
||||
.removalListener((RemovalListener<Player, ConverstionSession>) notification -> {
|
||||
if (notification.getKey() == null) return;
|
||||
USER_TYPE.invalidate(notification.getKey());
|
||||
if (notification.getCause() == RemovalCause.EXPIRED) {
|
||||
notification.getKey().sendMessage(
|
||||
PREFIX.append(Component.text("§cYour conversation has expired due to inactivity."))
|
||||
);
|
||||
}
|
||||
}).build();
|
||||
|
||||
new NPCService(NPCData.loadNPCData());
|
||||
|
||||
registerlisteners();
|
||||
registerCommands();
|
||||
|
||||
getLogger().log(Level.INFO, "Plugin enabled!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
getLogger().log(Level.INFO, "Plugin disabled!");
|
||||
}
|
||||
|
||||
private void registerCommands() {
|
||||
getCommand("conversation").setExecutor(new ConversationCommand());
|
||||
}
|
||||
|
||||
private void registerlisteners() {
|
||||
PluginManager pm = getServer().getPluginManager();
|
||||
|
||||
pm.registerEvents(new PlayerListener(), this);
|
||||
pm.registerEvents(new ChatHandler(), this);
|
||||
}
|
||||
|
||||
public static void startConversation(Player player, NPC npc, Type type) {
|
||||
if (CACHE.asMap().containsKey(player)) {
|
||||
CACHE.invalidate(player);
|
||||
|
||||
player.sendMessage(
|
||||
PREFIX.append(Component.text("§cYou are already in a conversation. Exiting the current conversation to start a new one."))
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
ConverstionSession session = new ConverstionSession(npc, player);
|
||||
|
||||
USER_TYPE.put(player, type);
|
||||
CACHE.put(player, session);
|
||||
|
||||
player.sendMessage(
|
||||
PREFIX.append(Component.text("§aYou have started a conversation with " + npc.getName() + "."))
|
||||
);
|
||||
}
|
||||
|
||||
public static NPCConversations getInstance() {
|
||||
return instance;
|
||||
}
|
||||
}
|
@ -0,0 +1,114 @@
|
||||
package net.voltexstudios.npcConversations.commands;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import net.voltexstudios.npcConversations.NPCConversations;
|
||||
import net.voltexstudios.npcConversations.npc.NPC;
|
||||
import net.voltexstudios.npcConversations.npc.NPCService;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.TabCompleter;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class ConversationCommand implements CommandExecutor, TabCompleter {
|
||||
|
||||
private static final int INTERACTION_ENTITY_RANGE = 5;
|
||||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command cmd, @NotNull String label, @NotNull String[] args) {
|
||||
|
||||
if (!sender.hasPermission("npcconversation.command")) {
|
||||
sender.sendMessage(
|
||||
NPCConversations.PREFIX.append(
|
||||
Component.text("§cYou do not have permission to use this command."))
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (args.length != 2) {
|
||||
sender.sendMessage(NPCConversations.PREFIX.append(
|
||||
Component.text("§cUsage: /conversation <create|delete> <name>"))
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(sender instanceof Player player)) {
|
||||
sender.sendMessage(
|
||||
NPCConversations.PREFIX.append(
|
||||
Component.text("§cOnly players can use this command."))
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (args[0].equalsIgnoreCase("delete")) {
|
||||
NPC npc = NPCService.getInstance().getNPC(args[1]);
|
||||
if (npc == null) {
|
||||
sender.sendMessage(
|
||||
NPCConversations.PREFIX.append(
|
||||
Component.text("§cNPC with given name not found."))
|
||||
);
|
||||
return false;
|
||||
}
|
||||
NPCService.getInstance().removeNPC(npc);
|
||||
NPCService.saveNPCData();
|
||||
|
||||
sender.sendMessage(
|
||||
NPCConversations.PREFIX.append(
|
||||
Component.text("§aNPC deleted successfully."))
|
||||
);
|
||||
return true;
|
||||
}
|
||||
if (args[0].equalsIgnoreCase("create")) {
|
||||
|
||||
Entity entity = player.getTargetEntity(INTERACTION_ENTITY_RANGE);
|
||||
if (entity == null) {
|
||||
player.sendMessage(
|
||||
NPCConversations.PREFIX.append(
|
||||
Component.text("§cYou must be looking at an entity to use this command."))
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
NPC npc = new NPC(args[1], entity.getUniqueId());
|
||||
|
||||
NPCService.getInstance().addNPC(npc);
|
||||
NPCService.saveNPCData();
|
||||
|
||||
sender.sendMessage(
|
||||
NPCConversations.PREFIX.append(
|
||||
Component.text("§aNPC created successfully.")
|
||||
.appendNewline()
|
||||
.append(Component.text("§7Name: ").append(Component.text(npc.getName()).color(NamedTextColor.BLUE)))
|
||||
.appendNewline()
|
||||
.append(Component.text("To edit the background story, go to the npcData.json file.").color(NamedTextColor.GRAY))
|
||||
)
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
sender.sendMessage(NPCConversations.PREFIX.append(
|
||||
Component.text("§cUsage: /conversation <create|delete> <name>"))
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable List<String> onTabComplete(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s, @NotNull String[] strings) {
|
||||
if (strings.length == 1) {
|
||||
return List.of("create", "delete");
|
||||
}
|
||||
if (strings.length == 2) {
|
||||
if (strings[0].equalsIgnoreCase("delete")) {
|
||||
return NPCService.getInstance().getNpcs().stream().map(NPC::getName).toList();
|
||||
}
|
||||
}
|
||||
return List.of();
|
||||
}
|
||||
}
|
@ -0,0 +1,84 @@
|
||||
|
||||
package net.voltexstudios.npcConversations.listener;
|
||||
|
||||
import com.launchableinc.openai.completion.chat.ChatMessage;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import net.voltexstudios.npcConversations.NPCConversations;
|
||||
import net.voltexstudios.npcConversations.session.ConverstionSession;
|
||||
import net.voltexstudios.npcConversations.util.Messages;
|
||||
import net.voltexstudios.npcConversations.util.OpenAI;
|
||||
import net.voltexstudios.npcConversations.util.Type;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.AsyncPlayerChatEvent;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class ChatHandler implements Listener {
|
||||
|
||||
@EventHandler
|
||||
public void onAsyncPlayerChat(AsyncPlayerChatEvent event) {
|
||||
Player player = event.getPlayer();
|
||||
|
||||
boolean hasFull = NPCConversations.USER_TYPE.asMap().values().stream().anyMatch(type -> type == Type.FULL);
|
||||
if (!NPCConversations.CACHE.asMap().containsKey(player) && !hasFull) {
|
||||
return;
|
||||
}
|
||||
|
||||
Collection<Player> recipients = switch (NPCConversations.USER_TYPE.asMap().getOrDefault(player, hasFull ? Type.FULL : Type.SINGLE)) {
|
||||
case SINGLE -> Collections.singletonList(player);
|
||||
case FULL, BROADCAST -> event.getRecipients();
|
||||
};
|
||||
|
||||
List<String> list = NPCConversations.getInstance().getConfig().getStringList("format");
|
||||
|
||||
if (!NPCConversations.getInstance().getConfig().getBoolean("use-default-chat", false)) {
|
||||
event.setCancelled(true);
|
||||
|
||||
sendMessage(format(list.getFirst(), event.getMessage(), player.getName(), ""), recipients);
|
||||
}
|
||||
|
||||
ConverstionSession session = NPCConversations.CACHE.getIfPresent(player);
|
||||
if (session == null) return;
|
||||
|
||||
List<ChatMessage> messages = session.getMessages();
|
||||
|
||||
OpenAI.getResponse(NPCConversations.getInstance().getConfig().getConfigurationSection("chatgpt"), messages, event.getMessage()).whenComplete((response, throwable) -> {
|
||||
if (throwable != null) {
|
||||
throwable.printStackTrace();
|
||||
player.sendMessage(
|
||||
NPCConversations.PREFIX.append(
|
||||
NPCConversations.PREFIX.append(
|
||||
Component.text("An error occurred while processing your message.").color(NamedTextColor.RED)
|
||||
)
|
||||
)
|
||||
);
|
||||
return;
|
||||
}
|
||||
sendMessage(format(list.get(1), response, player.getName(), session.getNpc().getName()), recipients);
|
||||
});
|
||||
}
|
||||
|
||||
private String format(String str, String message, String player, String npcName) {
|
||||
return Messages.format(str).replace("%message%", message).replace("%player%", player).replace("%npc%", npcName);
|
||||
}
|
||||
|
||||
private void sendMessage(String message, Collection<Player> players) {
|
||||
Bukkit.getOnlinePlayers().stream()
|
||||
.filter(player -> !players.contains(player) && player.hasPermission("minecraftgpt.receive"))
|
||||
.forEach(player -> player.sendMessage(message));
|
||||
|
||||
for (Player player : players)
|
||||
player.sendMessage(message);
|
||||
|
||||
if (NPCConversations.getInstance().getConfig().getBoolean("send-messages-to-console", true))
|
||||
NPCConversations.getInstance().getServer().getConsoleSender().sendMessage(message);
|
||||
}
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
package net.voltexstudios.npcConversations.listener;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import net.voltexstudios.npcConversations.NPCConversations;
|
||||
import net.voltexstudios.npcConversations.npc.NPC;
|
||||
import net.voltexstudios.npcConversations.npc.NPCService;
|
||||
import net.voltexstudios.npcConversations.session.ConverstionSession;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.PlayerInteractEntityEvent;
|
||||
import org.bukkit.event.player.PlayerMoveEvent;
|
||||
|
||||
public class PlayerListener implements Listener {
|
||||
|
||||
@EventHandler
|
||||
public void onEntityInteract(PlayerInteractEntityEvent event) {
|
||||
NPC npc = NPCService.getInstance().getNPC(event.getRightClicked());
|
||||
if (npc == null) return;
|
||||
|
||||
event.setCancelled(true);
|
||||
npc.onClick(event.getPlayer());
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onMove(PlayerMoveEvent event) {
|
||||
if (NPCConversations.CACHE.asMap().containsKey(event.getPlayer())) {
|
||||
|
||||
if (event.getFrom().getBlock().equals(event.getTo().getBlock())) return;
|
||||
|
||||
ConverstionSession session = NPCConversations.CACHE.getIfPresent(event.getPlayer());
|
||||
if (session == null) return;
|
||||
|
||||
NPC npc = session.getNpc();
|
||||
Entity entity = Bukkit.getEntity(npc.getClickableEntity());
|
||||
if (entity == null) return;
|
||||
|
||||
if (event.getTo().distance(entity.getLocation()) > NPCConversations.getInstance().getConfig().getInt("npc-conversation-range", 5)) {
|
||||
NPCConversations.CACHE.invalidate(event.getPlayer());
|
||||
event.getPlayer().sendMessage(
|
||||
NPCConversations.PREFIX.append(
|
||||
Component.text("You have moved too far away from the NPC. The conversation has been cancelled.").color(NamedTextColor.RED)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package net.voltexstudios.npcConversations.npc;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public interface Clickable {
|
||||
|
||||
void onClick(Player player);
|
||||
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
package net.voltexstudios.npcConversations.npc;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import net.voltexstudios.npcConversations.NPCConversations;
|
||||
import net.voltexstudios.npcConversations.util.Type;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@Getter
|
||||
public class NPC implements Clickable {
|
||||
|
||||
private final String name;
|
||||
private final UUID clickableEntity;
|
||||
@Setter private String backgroundStory;
|
||||
|
||||
public NPC(String name, UUID clickableEntity, String backgroundStory) {
|
||||
this.name = name;
|
||||
this.clickableEntity = clickableEntity;
|
||||
this.backgroundStory = backgroundStory;
|
||||
}
|
||||
|
||||
public NPC(String name, UUID clickableEntity) {
|
||||
this.name = name;
|
||||
this.clickableEntity = clickableEntity;
|
||||
this.backgroundStory = generateGenericBackgroundStory();
|
||||
}
|
||||
|
||||
private String generateGenericBackgroundStory() {
|
||||
return "Hello! I am " + this.name + ". I am a friendly NPC Assistant on this Minecraft Server. I am here to help you with your requests!";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(Player player) {
|
||||
NPCConversations.startConversation(player, this, Type.SINGLE);
|
||||
}
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
package net.voltexstudios.npcConversations.npc;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import java.util.Set;
|
||||
|
||||
public class NPCData {
|
||||
|
||||
private static final Gson gson = new GsonBuilder().setPrettyPrinting().create();
|
||||
private static final String npcDataPath = "plugins/NPCConversations/npcData.json";
|
||||
|
||||
private final Set<NPC> npcs;
|
||||
|
||||
public NPCData(Set<NPC> npcs) {
|
||||
this.npcs = npcs;
|
||||
}
|
||||
|
||||
public static void saveNPCData() {
|
||||
File folder = new File("plugins/NPCConversations");
|
||||
|
||||
if(!folder.exists()) {
|
||||
folder.mkdirs();
|
||||
}
|
||||
|
||||
try(FileWriter fileWriter = new FileWriter(npcDataPath)) {
|
||||
gson.toJson(NPCService.getInstance().getNPCData(), fileWriter);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static NPCData loadNPCData() {
|
||||
try(FileReader fileReader = new FileReader(npcDataPath)) {
|
||||
return gson.fromJson(fileReader, NPCData.class);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public Set<NPC> npcs() {
|
||||
return npcs;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
package net.voltexstudios.npcConversations.npc;
|
||||
|
||||
import lombok.Getter;
|
||||
import org.bukkit.entity.Entity;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
@Getter
|
||||
public final class NPCService {
|
||||
|
||||
private static NPCService instance;
|
||||
private final Set<NPC> npcs;
|
||||
|
||||
public NPCService(NPCData npcData) {
|
||||
instance = this;
|
||||
|
||||
if (npcData == null) {
|
||||
this.npcs = new HashSet<>();
|
||||
return;
|
||||
}
|
||||
|
||||
this.npcs = npcData.npcs();
|
||||
}
|
||||
|
||||
public NPC getNPC(String name) {
|
||||
return npcs.stream().filter(npc -> npc.getName().equals(name)).findFirst().orElse(null);
|
||||
}
|
||||
|
||||
public NPC getNPC(Entity entity) {
|
||||
return npcs.stream().filter(npc -> npc.getClickableEntity().equals(entity.getUniqueId())).findFirst().orElse(null);
|
||||
}
|
||||
|
||||
public void addNPC(NPC npc) {
|
||||
npcs.add(npc);
|
||||
}
|
||||
|
||||
public void removeNPC(NPC npc) {
|
||||
npcs.remove(npc);
|
||||
}
|
||||
|
||||
public NPCData getNPCData() {
|
||||
return new NPCData(npcs);
|
||||
}
|
||||
|
||||
public static void saveNPCData() {
|
||||
NPCData.saveNPCData();
|
||||
}
|
||||
|
||||
public static NPCService getInstance() {
|
||||
if (instance == null) {
|
||||
instance = new NPCService(null);
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
package net.voltexstudios.npcConversations.session;
|
||||
|
||||
import com.launchableinc.openai.completion.chat.ChatMessage;
|
||||
import lombok.Getter;
|
||||
import net.voltexstudios.npcConversations.npc.NPC;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Getter
|
||||
public class ConverstionSession {
|
||||
|
||||
private final List<ChatMessage> messages;
|
||||
private final NPC npc;
|
||||
private final Player player;
|
||||
|
||||
public ConverstionSession(@NotNull NPC npc, @NotNull Player player) {
|
||||
this.npc = npc;
|
||||
this.player = player;
|
||||
messages = new ArrayList<>();
|
||||
|
||||
messages.add(new ChatMessage("user", "Create a new persona for yourself. Once set, you can't change it anymore. " +
|
||||
"Stick to that persona at all times and react to me as if you were that persona." +
|
||||
"Also describe your background story. Who are you? What are you doing here? If you get asked a question that does not fit your persona, say that you can't answer that question." +
|
||||
"Also keep the conversation on a friendly level and don't be to serious."
|
||||
));
|
||||
messages.add(new ChatMessage("assistant", npc.getBackgroundStory()));
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package net.voltexstudios.npcConversations.util;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class Messages {
|
||||
|
||||
public static @NotNull String format(@Nullable String str) {
|
||||
if (str == null) return "Error - Bad Config";
|
||||
return str.replace("&", "§");
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
package net.voltexstudios.npcConversations.util;
|
||||
|
||||
import com.launchableinc.openai.completion.chat.ChatCompletionRequest;
|
||||
import com.launchableinc.openai.completion.chat.ChatMessage;
|
||||
import com.launchableinc.openai.service.OpenAiService;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import retrofit2.HttpException;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class OpenAI {
|
||||
|
||||
private static OpenAiService service;
|
||||
|
||||
public static CompletableFuture<Void> init(String key) {
|
||||
return CompletableFuture.runAsync(() -> service = new OpenAiService(key, Duration.ofSeconds(5)));
|
||||
}
|
||||
|
||||
public static CompletableFuture<String> getResponse(ConfigurationSection section, List<ChatMessage> chatMessages, String message) {
|
||||
chatMessages.add(new ChatMessage("user", message));
|
||||
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
String model = section.getString("model", "text-davinci-003");
|
||||
int maxTokens = section.getInt("max-tokens");
|
||||
double frequencyPenalty = section.getDouble("frequency-penalty");
|
||||
double presencePenalty = section.getDouble("presence-penalty");
|
||||
double topP = section.getDouble("top-p");
|
||||
double temperature = section.getDouble("temperature");
|
||||
|
||||
String reply = service.createChatCompletion(ChatCompletionRequest.builder()
|
||||
.messages(chatMessages)
|
||||
.model(model)
|
||||
.temperature(temperature)
|
||||
.maxTokens(maxTokens)
|
||||
.topP(topP)
|
||||
.frequencyPenalty(frequencyPenalty)
|
||||
.presencePenalty(presencePenalty)
|
||||
.stop(Arrays.asList("Human:", "AI:"))
|
||||
.build())
|
||||
.getChoices().getFirst().getMessage().getContent();
|
||||
|
||||
chatMessages.add(new ChatMessage("assistant", reply));
|
||||
return reply;
|
||||
}).exceptionally(throwable -> {
|
||||
if (throwable.getCause() instanceof HttpException e) {
|
||||
String reason = switch (e.response().code()) {
|
||||
case 401 -> "Invalid API key! Please check your configuration.";
|
||||
case 429 -> "Too many requests! Please wait a few seconds and try again.";
|
||||
case 500 -> "OpenAI service is currently unavailable. Please try again later.";
|
||||
default -> "Unknown error! Please try again later. If this error persists, contact the plugin developer.";
|
||||
};
|
||||
throw new RuntimeException(reason, throwable);
|
||||
}
|
||||
throw new RuntimeException(throwable);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package net.voltexstudios.npcConversations.util;
|
||||
|
||||
public enum Type {
|
||||
|
||||
SINGLE,
|
||||
BROADCAST,
|
||||
FULL;
|
||||
|
||||
public static Type getType(String type) {
|
||||
return switch (type.toLowerCase()) {
|
||||
case "single" -> Type.SINGLE;
|
||||
case "broadcast" -> Type.BROADCAST;
|
||||
case "full" -> Type.FULL;
|
||||
default -> null;
|
||||
};
|
||||
}
|
||||
|
||||
}
|
26
src/main/resources/config.yml
Normal file
26
src/main/resources/config.yml
Normal file
@ -0,0 +1,26 @@
|
||||
# https://beta.openai.com/account/api-keys
|
||||
API-KEY: "key"
|
||||
|
||||
chatgpt:
|
||||
# https://platform.openai.com/docs/models/
|
||||
model: "gpt-3.5-turbo"
|
||||
temperature: 0.9
|
||||
max-tokens: 150
|
||||
top-p: 1.0
|
||||
frequency-penalty: 0.0
|
||||
presence-penalty: 0.6
|
||||
|
||||
# the format of the conversation messages
|
||||
format:
|
||||
- "&b%player%: &7%message%"
|
||||
- "&b%npc%: &a%message%"
|
||||
|
||||
use-default-chat: false
|
||||
send-messages-to-console: true
|
||||
|
||||
|
||||
# how long an inactive conversation will last before it expires (in minutes)
|
||||
conversation-expiration-duration: 3
|
||||
|
||||
# the range in which a player can talk to an NPC (in blocks)
|
||||
npc-conversation-range: 5
|
12
src/main/resources/plugin.yml
Normal file
12
src/main/resources/plugin.yml
Normal file
@ -0,0 +1,12 @@
|
||||
name: NPCConversations
|
||||
version: '1.0'
|
||||
main: net.voltexstudios.npcConversations.NPCConversations
|
||||
api-version: '1.21'
|
||||
authors: [ VoltexStudios ]
|
||||
website: https://voltexstudios.net
|
||||
|
||||
commands:
|
||||
conversation:
|
||||
description: Manage Conversation NPC's
|
||||
permission: npcconversation.command
|
||||
aliases: [conv, talk]
|
26
target/classes/config.yml
Normal file
26
target/classes/config.yml
Normal file
@ -0,0 +1,26 @@
|
||||
# https://beta.openai.com/account/api-keys
|
||||
API-KEY: "key"
|
||||
|
||||
chatgpt:
|
||||
# https://platform.openai.com/docs/models/
|
||||
model: "gpt-3.5-turbo"
|
||||
temperature: 0.9
|
||||
max-tokens: 150
|
||||
top-p: 1.0
|
||||
frequency-penalty: 0.0
|
||||
presence-penalty: 0.6
|
||||
|
||||
# the format of the conversation messages
|
||||
format:
|
||||
- "&b%player%: &7%message%"
|
||||
- "&b%npc%: &a%message%"
|
||||
|
||||
use-default-chat: false
|
||||
send-messages-to-console: true
|
||||
|
||||
|
||||
# how long an inactive conversation will last before it expires (in minutes)
|
||||
conversation-expiration-duration: 3
|
||||
|
||||
# the range in which a player can talk to an NPC (in blocks)
|
||||
npc-conversation-range: 5
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
target/classes/net/voltexstudios/npcConversations/npc/NPC.class
Normal file
BIN
target/classes/net/voltexstudios/npcConversations/npc/NPC.class
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
12
target/classes/plugin.yml
Normal file
12
target/classes/plugin.yml
Normal file
@ -0,0 +1,12 @@
|
||||
name: NPCConversations
|
||||
version: '1.0'
|
||||
main: net.voltexstudios.npcConversations.NPCConversations
|
||||
api-version: '1.21'
|
||||
authors: [ VoltexStudios ]
|
||||
website: https://voltexstudios.net
|
||||
|
||||
commands:
|
||||
conversation:
|
||||
description: Manage Conversation NPC's
|
||||
permission: npcconversation.command
|
||||
aliases: [conv, talk]
|
3
target/maven-archiver/pom.properties
Normal file
3
target/maven-archiver/pom.properties
Normal file
@ -0,0 +1,3 @@
|
||||
artifactId=npcconversations
|
||||
groupId=net.voltexstudios
|
||||
version=1.0
|
@ -0,0 +1,13 @@
|
||||
net/voltexstudios/npcConversations/session/ConverstionSession.class
|
||||
net/voltexstudios/npcConversations/npc/NPCData.class
|
||||
net/voltexstudios/npcConversations/listener/PlayerListener.class
|
||||
net/voltexstudios/npcConversations/listener/ChatHandler.class
|
||||
net/voltexstudios/npcConversations/npc/Clickable.class
|
||||
net/voltexstudios/npcConversations/util/OpenAI.class
|
||||
net/voltexstudios/npcConversations/listener/ChatHandler$1.class
|
||||
net/voltexstudios/npcConversations/npc/NPCService.class
|
||||
net/voltexstudios/npcConversations/util/Type.class
|
||||
net/voltexstudios/npcConversations/npc/NPC.class
|
||||
net/voltexstudios/npcConversations/NPCConversations.class
|
||||
net/voltexstudios/npcConversations/util/Messages.class
|
||||
net/voltexstudios/npcConversations/commands/ConversationCommand.class
|
@ -0,0 +1,12 @@
|
||||
/home/paisc/Dokumente/tmp_workspace/Voltex/NPCConversations/src/main/java/net/voltexstudios/npcConversations/NPCConversations.java
|
||||
/home/paisc/Dokumente/tmp_workspace/Voltex/NPCConversations/src/main/java/net/voltexstudios/npcConversations/commands/ConversationCommand.java
|
||||
/home/paisc/Dokumente/tmp_workspace/Voltex/NPCConversations/src/main/java/net/voltexstudios/npcConversations/listener/ChatHandler.java
|
||||
/home/paisc/Dokumente/tmp_workspace/Voltex/NPCConversations/src/main/java/net/voltexstudios/npcConversations/listener/PlayerListener.java
|
||||
/home/paisc/Dokumente/tmp_workspace/Voltex/NPCConversations/src/main/java/net/voltexstudios/npcConversations/npc/Clickable.java
|
||||
/home/paisc/Dokumente/tmp_workspace/Voltex/NPCConversations/src/main/java/net/voltexstudios/npcConversations/npc/NPC.java
|
||||
/home/paisc/Dokumente/tmp_workspace/Voltex/NPCConversations/src/main/java/net/voltexstudios/npcConversations/npc/NPCData.java
|
||||
/home/paisc/Dokumente/tmp_workspace/Voltex/NPCConversations/src/main/java/net/voltexstudios/npcConversations/npc/NPCService.java
|
||||
/home/paisc/Dokumente/tmp_workspace/Voltex/NPCConversations/src/main/java/net/voltexstudios/npcConversations/session/ConverstionSession.java
|
||||
/home/paisc/Dokumente/tmp_workspace/Voltex/NPCConversations/src/main/java/net/voltexstudios/npcConversations/util/Messages.java
|
||||
/home/paisc/Dokumente/tmp_workspace/Voltex/NPCConversations/src/main/java/net/voltexstudios/npcConversations/util/OpenAI.java
|
||||
/home/paisc/Dokumente/tmp_workspace/Voltex/NPCConversations/src/main/java/net/voltexstudios/npcConversations/util/Type.java
|
BIN
target/npcconversations-1.0.jar
Normal file
BIN
target/npcconversations-1.0.jar
Normal file
Binary file not shown.
BIN
target/original-npcconversations-1.0.jar
Normal file
BIN
target/original-npcconversations-1.0.jar
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user