Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
CreatureChat
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Merge Requests
1
Merge Requests
1
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Jobs
Commits
Open sidebar
Public
CreatureChat
Commits
bfa46037
Commit
bfa46037
authored
Nov 09, 2023
by
Jonathan Thomas
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Initial chat screen UI, with Chat GPT integration! Full circle achieved!
parent
485c80e8
Pipeline
#11656
passed with stage
in 21 seconds
Changes
6
Pipelines
1
Show whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
203 additions
and
12 deletions
+203
-12
ChatScreen.java
src/client/java/com/owlmaddie/ChatScreen.java
+117
-0
ClickHandler.java
src/client/java/com/owlmaddie/ClickHandler.java
+8
-2
ModPackets.java
src/client/java/com/owlmaddie/ModPackets.java
+17
-0
ChatDataManager.java
src/main/java/com/owlmaddie/ChatDataManager.java
+19
-7
ChatGPTRequest.java
src/main/java/com/owlmaddie/ChatGPTRequest.java
+2
-2
ModInit.java
src/main/java/com/owlmaddie/ModInit.java
+40
-1
No files found.
src/client/java/com/owlmaddie/ChatScreen.java
0 → 100644
View file @
bfa46037
package
com
.
owlmaddie
;
import
net.minecraft.client.gui.DrawContext
;
import
net.minecraft.client.gui.screen.Screen
;
import
net.minecraft.client.gui.widget.ButtonWidget
;
import
net.minecraft.client.gui.widget.TextFieldWidget
;
import
net.minecraft.entity.Entity
;
import
net.minecraft.text.Text
;
import
org.lwjgl.glfw.GLFW
;
public
class
ChatScreen
extends
Screen
{
private
TextFieldWidget
textField
;
private
ButtonWidget
sendButton
;
private
ButtonWidget
cancelButton
;
private
Entity
screenEntity
;
private
final
Text
labelText
=
Text
.
literal
(
"Enter your message:"
);
public
ChatScreen
(
Entity
entity
)
{
super
(
Text
.
literal
(
"Simple Chat"
));
screenEntity
=
entity
;
}
@Override
protected
void
init
()
{
super
.
init
();
// Centered text field dimensions
int
textFieldWidth
=
220
;
int
textFieldHeight
=
20
;
int
textFieldX
=
(
this
.
width
-
textFieldWidth
)
/
2
;
// Centered X position
int
textFieldY
=
100
;
// Y position
// Initialize the text field
textField
=
new
TextFieldWidget
(
textRenderer
,
textFieldX
,
textFieldY
,
textFieldWidth
,
textFieldHeight
,
Text
.
literal
(
"Chat Input"
));
textField
.
setMaxLength
(
512
);
textField
.
setDrawsBackground
(
true
);
textField
.
setText
(
""
);
this
.
addDrawableChild
(
textField
);
// Set focus to the text field
setFocused
(
textField
);
// Set the text field as the focused element
textField
.
setFocused
(
true
);
// Request focus for the text field
// Button dimensions and positions
int
buttonWidth
=
100
;
int
buttonHeight
=
20
;
int
buttonSpacing
=
20
;
// Space between buttons
int
buttonsY
=
textFieldY
+
textFieldHeight
+
15
;
// Y position under the text field
// Initialize the cancel button
cancelButton
=
new
ButtonWidget
.
Builder
(
Text
.
literal
(
"Cancel"
),
button
->
close
())
.
size
(
buttonWidth
,
buttonHeight
)
.
position
(
textFieldX
,
buttonsY
)
.
build
();
this
.
addDrawableChild
(
cancelButton
);
// Initialize the send button
sendButton
=
new
ButtonWidget
.
Builder
(
Text
.
literal
(
"Send"
),
button
->
sendChatMessage
())
.
size
(
buttonWidth
,
buttonHeight
)
.
position
(
textFieldX
+
buttonWidth
+
buttonSpacing
,
buttonsY
)
.
build
();
this
.
addDrawableChild
(
sendButton
);
}
private
void
sendChatMessage
()
{
// Send message to server
String
message
=
textField
.
getText
();
ModPackets
.
sendChat
(
screenEntity
,
message
);
close
();
}
@Override
public
boolean
keyPressed
(
int
keyCode
,
int
scanCode
,
int
modifiers
)
{
if
(
keyCode
==
GLFW
.
GLFW_KEY_ENTER
||
keyCode
==
GLFW
.
GLFW_KEY_KP_ENTER
)
{
if
(
textField
.
isFocused
())
{
// Close window on ENTER key press
sendChatMessage
();
return
true
;
}
}
return
super
.
keyPressed
(
keyCode
,
scanCode
,
modifiers
);
// Handle other key presses
}
@Override
public
void
render
(
DrawContext
context
,
int
mouseX
,
int
mouseY
,
float
delta
)
{
// Render the background
this
.
renderBackground
(
context
,
mouseX
,
mouseY
,
delta
);
// Render the label text above the text field
int
labelWidth
=
textRenderer
.
getWidth
(
labelText
);
int
labelX
=
(
this
.
width
-
labelWidth
)
/
2
;
// Centered X position
int
labelY
=
textField
.
getY
()
-
15
;
// Positioned above the text field
context
.
drawTextWithShadow
(
textRenderer
,
labelText
,
labelX
,
labelY
,
0xFFFFFF
);
// Render the text field
textField
.
render
(
context
,
mouseX
,
mouseY
,
delta
);
// Render the buttons
sendButton
.
render
(
context
,
mouseX
,
mouseY
,
delta
);
cancelButton
.
render
(
context
,
mouseX
,
mouseY
,
delta
);
// Call super.render if necessary
super
.
render
(
context
,
mouseX
,
mouseY
,
delta
);
}
@Override
public
boolean
shouldCloseOnEsc
()
{
// Return true if you want the screen to close when the ESC key is pressed
return
true
;
}
@Override
public
boolean
shouldPause
()
{
// Return false to prevent the game from pausing when the screen is open
return
false
;
}
}
src/client/java/com/owlmaddie/ClickHandler.java
View file @
bfa46037
...
...
@@ -43,6 +43,7 @@ public class ClickHandler {
String
message
=
buffer
.
readString
(
32767
);
int
line
=
buffer
.
readInt
();
String
status_name
=
buffer
.
readString
(
32767
);
String
sender_name
=
buffer
.
readString
(
32767
);
// Update the chat data manager on the client-side
client
.
execute
(()
->
{
// Make sure to run on the client thread
...
...
@@ -55,6 +56,7 @@ public class ClickHandler {
}
chatData
.
currentLineNumber
=
line
;
chatData
.
status
=
ChatDataManager
.
ChatStatus
.
valueOf
(
status_name
);
chatData
.
sender
=
ChatDataManager
.
ChatSender
.
valueOf
(
sender_name
);
}
});
});
...
...
@@ -131,12 +133,16 @@ public class ClickHandler {
// Look-up conversation
ChatDataManager
.
EntityChatData
chatData
=
ChatDataManager
.
getClientInstance
().
getOrCreateChatData
(
closestEntity
.
getId
());
if
(
chatData
.
currentMessage
.
isEmpty
()
)
{
if
(
chatData
.
status
==
ChatDataManager
.
ChatStatus
.
NONE
)
{
// Start conversation
ModPackets
.
sendGenerateGreeting
(
closestEntity
);
}
else
{
}
else
if
(
chatData
.
status
==
ChatDataManager
.
ChatStatus
.
DISPLAY
)
{
// Update lines read
ModPackets
.
sendUpdateLineNumber
(
closestEntity
,
chatData
.
currentLineNumber
+
ClientInit
.
DISPLAY_NUM_LINES
);
}
else
if
(
chatData
.
status
==
ChatDataManager
.
ChatStatus
.
END
)
{
// End of chat (open player chat screen)
ModPackets
.
sendStartChat
(
closestEntity
);
// Slow down entity while chat screen is open
client
.
setScreen
(
new
ChatScreen
(
closestEntity
));
}
}
...
...
src/client/java/com/owlmaddie/ModPackets.java
View file @
bfa46037
...
...
@@ -27,5 +27,22 @@ public class ModPackets {
// Send C2S packet
ClientPlayNetworking
.
send
(
ModInit
.
PACKET_C2S_READ_NEXT
,
buf
);
}
public
static
void
sendStartChat
(
Entity
entity
)
{
PacketByteBuf
buf
=
new
PacketByteBuf
(
Unpooled
.
buffer
());
buf
.
writeInt
(
entity
.
getId
());
// Send C2S packet
ClientPlayNetworking
.
send
(
ModInit
.
PACKET_C2S_START_CHAT
,
buf
);
}
public
static
void
sendChat
(
Entity
entity
,
String
message
)
{
PacketByteBuf
buf
=
new
PacketByteBuf
(
Unpooled
.
buffer
());
buf
.
writeInt
(
entity
.
getId
());
buf
.
writeString
(
message
);
// Send C2S packet
ClientPlayNetworking
.
send
(
ModInit
.
PACKET_C2S_SEND_CHAT
,
buf
);
}
}
src/main/java/com/owlmaddie/ChatDataManager.java
View file @
bfa46037
...
...
@@ -18,6 +18,12 @@ public class ChatDataManager {
END
// Chat has ended or been dismissed
}
public
enum
ChatSender
{
NONE
,
// A blank chat message
USER
,
// A user chat message
ASSISTANT
// A GPT generated message
}
// HashMap to associate unique entity IDs with their chat data
private
HashMap
<
Integer
,
EntityChatData
>
entityChatDataMap
;
...
...
@@ -29,6 +35,7 @@ public class ChatDataManager {
public
ChatStatus
status
;
public
List
<
String
>
previousMessages
;
public
String
characterSheet
;
public
ChatSender
sender
;
public
EntityChatData
(
int
entityId
)
{
this
.
entityId
=
entityId
;
...
...
@@ -37,14 +44,19 @@ public class ChatDataManager {
this
.
previousMessages
=
new
ArrayList
<>();
this
.
characterSheet
=
""
;
this
.
status
=
ChatStatus
.
NONE
;
this
.
sender
=
ChatSender
.
NONE
;
}
// Generate greeting
public
void
generate
Greeting
(
)
{
public
void
generate
Message
(
String
user_message
)
{
this
.
status
=
ChatStatus
.
PENDING
;
ChatGPTRequest
.
fetchGreetingFromChatGPT
().
thenAccept
(
greeting
->
{
if
(
greeting
!=
null
)
{
this
.
addMessage
(
greeting
);
// Add USER Message
//this.addMessage(user_message, ChatSender.USER);
ChatGPTRequest
.
fetchMessageFromChatGPT
(
user_message
).
thenAccept
(
output_message
->
{
if
(
output_message
!=
null
)
{
// Add ASSISTANT message
this
.
addMessage
(
output_message
,
ChatSender
.
ASSISTANT
);
}
});
...
...
@@ -53,9 +65,9 @@ public class ChatDataManager {
}
// Add a message to the history and update the current message
public
void
addMessage
(
String
message
)
{
public
void
addMessage
(
String
message
,
ChatSender
sender
)
{
if
(!
currentMessage
.
isEmpty
())
{
previousMessages
.
add
(
currentMessage
);
previousMessages
.
add
(
sender
.
toString
()
+
": "
+
currentMessage
);
}
currentMessage
=
message
;
...
...
@@ -76,7 +88,7 @@ public class ChatDataManager {
public
void
setLineNumber
(
Integer
lineNumber
)
{
// Update displayed starting line # (between 0 and # of lines)
currentLineNumber
=
Math
.
min
(
Math
.
max
(
lineNumber
,
0
),
this
.
getWrappedLines
().
size
());
if
(
lineNumber
=
=
this
.
getWrappedLines
().
size
())
{
if
(
currentLineNumber
>
=
this
.
getWrappedLines
().
size
())
{
status
=
ChatStatus
.
END
;
}
...
...
src/main/java/com/owlmaddie/ChatGPTRequest.java
View file @
bfa46037
...
...
@@ -16,7 +16,7 @@ import java.util.concurrent.CompletableFuture;
public
class
ChatGPTRequest
{
public
static
final
Logger
LOGGER
=
LoggerFactory
.
getLogger
(
"mobgpt"
);
public
static
CompletableFuture
<
String
>
fetch
GreetingFromChatGPT
(
)
{
public
static
CompletableFuture
<
String
>
fetch
MessageFromChatGPT
(
String
user_message
)
{
return
CompletableFuture
.
supplyAsync
(()
->
{
try
{
URL
url
=
new
URL
(
"https://api.openai.com/v1/chat/completions"
);
...
...
@@ -30,7 +30,7 @@ public class ChatGPTRequest {
+
"\"model\": \"gpt-3.5-turbo\","
+
"\"messages\": ["
+
"{ \"role\": \"system\", \"content\": \"You are a silly Minecraft entity who speaks to the player in short riddles.\" },"
+
"{ \"role\": \"user\", \"content\": \"
Hello!
\" }"
+
"{ \"role\": \"user\", \"content\": \"
"
+
user_message
.
replace
(
"\""
,
""
)
+
"
\" }"
+
"]"
+
"}"
;
LOGGER
.
info
(
jsonInputString
);
...
...
src/main/java/com/owlmaddie/ModInit.java
View file @
bfa46037
...
...
@@ -22,6 +22,8 @@ public class ModInit implements ModInitializer {
private
static
MinecraftServer
serverInstance
;
public
static
final
Identifier
PACKET_C2S_GREETING
=
new
Identifier
(
"mobgpt"
,
"packet_c2s_greeting"
);
public
static
final
Identifier
PACKET_C2S_READ_NEXT
=
new
Identifier
(
"mobgpt"
,
"packet_c2s_read_next"
);
public
static
final
Identifier
PACKET_C2S_START_CHAT
=
new
Identifier
(
"mobgpt"
,
"packet_c2s_start_chat"
);
public
static
final
Identifier
PACKET_C2S_SEND_CHAT
=
new
Identifier
(
"mobgpt"
,
"packet_c2s_send_chat"
);
public
static
final
Identifier
PACKET_S2C_MESSAGE
=
new
Identifier
(
"mobgpt"
,
"packet_s2c_message"
);
@Override
...
...
@@ -46,7 +48,7 @@ public class ModInit implements ModInitializer {
chatData
.
status
==
ChatDataManager
.
ChatStatus
.
END
)
{
// Only generate a new greeting if not already doing so
LOGGER
.
info
(
"Generate greeting for: "
+
entity
.
getType
().
toString
());
chatData
.
generate
Greeting
(
);
chatData
.
generate
Message
(
"Hello!"
);
}
}
});
...
...
@@ -74,6 +76,42 @@ public class ModInit implements ModInitializer {
});
});
// Handle packet for Start Chat
ServerPlayNetworking
.
registerGlobalReceiver
(
PACKET_C2S_START_CHAT
,
(
server
,
player
,
handler
,
buf
,
responseSender
)
->
{
int
entityId
=
buf
.
readInt
();
// Ensure that the task is synced with the server thread
server
.
execute
(()
->
{
Entity
entity
=
player
.
getServerWorld
().
getEntityById
(
entityId
);
if
(
entity
!=
null
)
{
// Slow entity, so it does NOT walk away during player typing
SlowEntity
((
LivingEntity
)
entity
,
7
F
);
}
});
});
// Handle packet for new chat message
ServerPlayNetworking
.
registerGlobalReceiver
(
PACKET_C2S_SEND_CHAT
,
(
server
,
player
,
handler
,
buf
,
responseSender
)
->
{
int
entityId
=
buf
.
readInt
();
String
message
=
buf
.
readString
(
32767
);
// Ensure that the task is synced with the server thread
server
.
execute
(()
->
{
Entity
entity
=
player
.
getServerWorld
().
getEntityById
(
entityId
);
if
(
entity
!=
null
)
{
// Slow entity
SlowEntity
((
LivingEntity
)
entity
,
3.5
F
);
ChatDataManager
.
EntityChatData
chatData
=
ChatDataManager
.
getServerInstance
().
getOrCreateChatData
(
entityId
);
if
(
chatData
.
status
==
ChatDataManager
.
ChatStatus
.
END
)
{
// Add new message
LOGGER
.
info
(
"Add new message ("
+
message
+
") to Entity: "
+
entity
.
getType
().
toString
());
chatData
.
generateMessage
(
message
);
}
}
});
});
ServerWorldEvents
.
LOAD
.
register
((
server
,
world
)
->
{
// Load chat data...
LOGGER
.
info
(
"LOAD chat data from NBT: "
+
world
.
getRegistryKey
().
getValue
());
...
...
@@ -100,6 +138,7 @@ public class ModInit implements ModInitializer {
buffer
.
writeString
(
chatData
.
currentMessage
);
buffer
.
writeInt
(
chatData
.
currentLineNumber
);
buffer
.
writeString
(
chatData
.
status
.
toString
());
buffer
.
writeString
(
chatData
.
sender
.
toString
());
// Iterate over all players and send the packet
for
(
ServerPlayerEntity
player
:
serverInstance
.
getPlayerManager
().
getPlayerList
())
{
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment