Skip to content

Commit

Permalink
update: refactored NPCEntity and Group
Browse files Browse the repository at this point in the history
  • Loading branch information
WDRshadow committed Mar 25, 2024
1 parent 1575363 commit 0bf7218
Show file tree
Hide file tree
Showing 26 changed files with 795 additions and 423 deletions.
15 changes: 9 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,19 @@

[Chinese Version](docs/README_zh.md)

> The mod is still under development, and the current version is not stable. If you want to use the mod, please compile
> The mod is still under development, and the current version is not stable. If you want to use the latest version,
> please compile
> the source code yourself, see [Build](#8-build). If you encounter any problems, please submit an issue.
## 1. Introduction

**Chat With NPC** allows players to freely chat with NPCs, who will answer questions based on his setting (Basic Prompt)
and the setting of their Groups (Nation, City, Town ,etc.). Suitable for RPG maps or puzzle maps. The NPCs would not
change the game rule, i.e. the actions of the entity are not affected by the chat content (either in Adventure or
Survival modes), that means it can be used even on Survival servers to make the game more interesting.
change the game rule (if you don't use the advanced features). NPC can also perform some actions based on the chat
content if you define it.

In the future version, we will add innovative features such as NPCs performing actions based on chat content, NPCs
chatting with each other, NPCs' viewpoints on events, etc., so that NPCs in the same Group can interact with each other.
In the future version, we will add innovative features such as NPCs chatting with each other, NPCs' viewpoints on
events, etc., so that NPCs in the same Group can interact with each other.

## 2. How to use

Expand Down Expand Up @@ -45,7 +46,9 @@ If you are administrator, see the [Installation](#5-installation) for more infor
4. Each `Group` can record a `ParentGroup` until the group's parent group is `Global`, `Global` cannot have a parent
group.
5. Each `NPC`'s conversation will combine his own `Instructions`, the context of this
conversation (or all the conversation if needMemory is true), the `PermanentPrompt` of his `Group` and all its `ParentGroup`, and the content of `TempEvent`.
conversation (or all the conversation if needMemory is true), the `PermanentPrompt` of his `Group` and all
its `ParentGroup`, and the content of `TempEvent`.
6. You can define the `Functions` that could be call by the NPC in the conversation.

## 4. Requirements

Expand Down
157 changes: 125 additions & 32 deletions docs/Advanced.md
Original file line number Diff line number Diff line change
@@ -1,45 +1,69 @@
# Advance Features
# Advanced Features

## 1. Functions Calling
## 1. Function Calls

Function calling is an advanced feature of the OpenAI API that allows ChatGPT to call functions that may affect game balance as an NPC at the appropriate time. The functions are defined by the server administrator, and the effects that can be achieved are determined by you. For a detailed introduction to this feature, please refer to the [OpenAI API documentation](https://beta.openai.com/docs/api-reference/function-calls/create-function-call).
Function calls are an advanced feature of the OpenAI API that allows ChatGPT to call functions that may affect game
balance as an NPC at the appropriate time. The functions are defined by the server administrator, and the effects that
can be achieved are determined by you. For more information on this feature, see
the [OpenAI API documentation](https://beta.openai.com/docs/api-reference/function-calls/create-function-call).

For example, you can define a function named `give_diamond`. When a player talks to an NPC and expresses a desire for diamonds, the NPC can call the `give_diamond` function to implement this feature. For example, you can define some more complex functions to implement "bargaining" between players and NPCs, and so on.
For example, you can define a function called `give_diamond` that allows an NPC to call the `give_diamond` function when
a player talks to the NPC and expresses a desire for diamonds. You can also define more complex functions to implement
player-NPC "bargaining" interactions, etc.

The following commands can be used to add or remove functions that NPCs can call in the game:
Functions can be added or removed from NPCs using the following commands:

- `/npchat npc addFunction <function>` - Add a function to the NPC closest to you
- `/npchat npc deleteFunction <function>` - Remove a function from the NPC closest to you

**_!! Please note:_** These functions must be registered (in the code or in the configuration file) to take effect, otherwise it will affect the operation of the entire mod.
**_!! Please note:_** These functions must be registered (in the code or in the configuration file) to take effect,
otherwise it will affect the operation of the entire mod.

Function calls can be implemented in two ways:

### 1. Implemented through code based on this mod's API
### 1. Implemented through code based on this mod API

This method requires you to write some mod code on the server side to implement the functions you define. The advantage of this method is that it can implement very complex functions, but the disadvantage is that it requires you to have some mod development experience.
This method requires you to write some mod code on the server side to implement the functions you define. The advantage
of this method is that it can implement very complex functions, but the disadvantage is that it requires you to have
some mod development experience.

>We will open the mod API documentation later so that you can better develop secondary based on this mod API. At present, you can clone the source code of this mod, view the comments of the [`src/main/java/com/jackdaw/chatwithnpc/openaiapi/functioncalling/CustomFunction`](../src/main/java/com/jackdaw/chatwithnpc/openaiapi/function/CustomFunction.java) class, inherit this class, register the function, and compile this plugin to obtain the function.
> We will open the mod API documentation later so that you can better develop based on this mod API. For now, you can
> clone the source code of this mod and view the comments of
> the [`src/main/java/com/jackdaw/chatwithnpc/openaiapi/function/CustomFunction`](../src/main/java/com/jackdaw/chatwithnpc/openaiapi/function/CustomFunction.java)
> class, inherit this class and register the function to compile this plugin to get the function.
> function.
You can clone the template repository [NPCBasicFunction](https://github.com/Team-Jackdaw/NPCBasicFunction) to view the
example code.

Here is a simple example:

```java

public class GiveDiamondFunction extends CustomFunction {

public GiveDiamondFunction() {
description = "This function is used to give player a diamond. If the player make a request to the NPC to give them some diamonds, this function will be called.";
description = "This function is used to give player a diamond. You can give player diamonds if you want.";
properties = Map.of(
"number", "the number of diamonds to give to the player."
"number", Map.of(
"type", "integer",
"description", "the number of diamonds to give to the player."
)
);
}

@Override
public Map<String, String> execute(@NotNull ConversationHandler conversation, @NotNull Map<String, String> args) {
public Map<String, String> execute(@NotNull ConversationHandler conversation, @NotNull Map<String, Object> args) {
int number;
try{
number = Integer.parseInt(args.get("number"));
} catch (NumberFormatException e) {
number = 1;
try {
number = (int) args.get("number");
} catch (ClassCastException ignore) {
try {
double doubleNumber = Double.parseDouble(args.get("number").toString());
number = (int) doubleNumber;
} catch (NumberFormatException ignore2) {
number = 1;
}
}
ItemStack diamond = new ItemStack(Items.DIAMOND, number);
conversation.getNpc().findNearbyPlayers(10).forEach(player -> player.giveItemStack(diamond));
Expand All @@ -48,12 +72,16 @@ public class GiveDiamondFunction extends CustomFunction {
}
```

In this example, we define a function named `give_diamond`, which is used to give the player a certain number of diamonds. This function accepts a parameter `number`, which represents how many diamonds to give to the player. In the `execute` method, this function is implemented. To teach ChatGPT to call this function, we need to write the function's purpose in the `description` and explain the `properties` of the parameters in the constructor (the parameters are stored in the form of a Map).
In this example, we define a function named `give_diamond` that gives the player a certain number of diamonds. This
function takes a parameter `number`, which represents how many diamonds to give to the player. In the `execute` method,
this function is implemented. To teach ChatGPT to call this function, we need to describe the function's purpose in
the `description` in the constructor and explain the `properties` of the parameters in the form of a Map.

Don't forget to register this function in the static initialization method of the mod:

```java
public class ChatWithNPCMod implements ModInitializer {

public class NPCBasicFunction implements ModInitializer {
@Override
public void onInitialize() {
// ...
Expand All @@ -63,42 +91,107 @@ public class ChatWithNPCMod implements ModInitializer {
}
```

**_Effect picture:_**
**_Effect Picture:_**

![give_diamond](images/give_diamond.png)

### 2. Implemented through the configuration file
### 2. Implemented through configuration files

This method does not require you to write any code, just define the function's purpose and parameters in the configuration file. The advantage of this method is that it is simple and easy to use, but the disadvantage is that this method itself cannot be implemented and executed, but information can be passed to the NBT data of the NPC entity through parameters. Thus, function calls can be implemented in the game through "modify map data packet", "command block", and other methods (not shown here).
This method does not require you to write any Java code. You only need to define the function's purpose and parameters
in the configuration file and write the Minecraft command-related function in the map data packet in the map. The
advantage of this method is that it is simple and easy to use, but the disadvantage is that this method cannot return
complex results to OpenAI.

Here is a simple example:

Create a file named `setNPCHappy.json` in the `config/chatwithnpc/functions` directory with the following content:
Create a file named `open_door.json` in the `config/chatwithnpc/functions` directory with the following content:

```json
{
"type": "function",
"function": {
"name": "setNPCHappy",
"description": "This function is used to indicate that the NPC is happy now.",
"name": "open_door",
"description": "This function is used to let you open the door or close the door.",
"parameters": {
"type": "object",
"properties": {
"level": {
"type": "string",
"description": "How happy is the NPC, choose a floating point number from 0 to 1."
"willingness": {
"type": "integer",
"description": "If this parameter is 0, you will close the door. If it is 1, you will open the door.",
"enum": [0, 1]
}
},
"required": ["level"]
"required": [
"willingness"
]
}
}
},
"call": "npchat:open_door"
}
```

In this example, we define a function named `setNPCHappy`, which is used to make the NPC happy (in fact, happiness is just a key-value pair recorded in the NPC's NBT data). This function accepts a parameter `level`, which represents how happy the NPC is. To teach ChatGPT to call this function, we need to write the function's purpose in the `description` and explain the `properties` of the parameters.
In this example, we define a function named `open_door` that allows the NPC to open or close the door. This function
takes a parameter `willingness`, which indicates whether the NPC wants to open the door (`1`) or close the door (`0`).
To teach ChatGPT to call this function, we need to describe the function's purpose in the `description` and explain
the `properties` of the parameters.

When the NPC calls the function, it will first create one or more scoreboards `npc_<function_name>_<arg>` on the player
closest to the NPC to record the function parameters called by the NPC, then assign them to the player according to the
value of the parameters, and finally read the result scoreboard `npc_<function_name>_result`.
If the scoreboard is created and the result of the nearest player is 0, it means failure, and the result will affect the
NPC's response. If the function is not called or the scoreboard is not created or the scoreboard is 1, the result is successful.

Acceptable parameters:

- `type`: The type of the function, fixed as `function`
- `function`
- `name`: The name of the function
- `description`: The purpose of the function
- `parameters`
- `type`: The type of the parameter, fixed as `object`
- `properties`
- `key`: The name of the parameter (custom)
- `type`: The type of the parameter, fixed as `integer`, and recorded in the scoreboard
- `description`: The purpose of the parameter
- `enum`: Optional values of the parameter (optional)
- `required`: Parameters that the NPC must fill in (optional)
- `call`: Minecraft map data packet Function for function calls (optional)

In addition, we also need to specify the Minecraft map data packet Function for the function call in `call`, in the form
of `<name_space>:<function_name>`. Please refer to the relevant Minecraft documentation for how to write the map data
packet.

Here is a simple example:

```mcfunction
# (In the directory /server/world/datapacks/npchat/data/npchat/functions/open_door.mcfunction)
# The parameter is 'npc_open_door_willingness', 0 means close the door, 1 means open the door
# Reset the player's result
execute run scoreboard players set @s npc_open_door_result 0
# Create a result scoreboard, which will be read by gpt
execute run scoreboard objectives add npc_open_door_result dummy
# Some other steps, such as checking if the player has permission, if there is a key, etc.
# ...
# Check the parameter, if it is 1, open the door (by removing a redstone block)
execute if entity @p[scores={npc_open_door_willingness=1}] run setblock -3812 125 2831 air
# Check the parameter, if it is 0, close the door (by placing a redstone block)
execute if entity @p[scores={npc_open_door_willingness=0}] run setblock -3812 125 2831 redstone_block
# After successful execution, set the result to 1
execute run scoreboard players set @s npc_open_door_result 1
```

All functions in the `config/chatwithnpc/functions` directory will be automatically registered when the server starts.
If you need to hot reload during server startup, you can use `/npchat saveAll` to reload.

Don't forget to add this function to the NPC.

**_Effect picture:_**
**_Effect Picture:_**

![setNPCHappy](images/setNPCHappy.png)
![open_door](images/open_door.png)
![close_door](images/close_door.png)
Loading

0 comments on commit 0bf7218

Please sign in to comment.