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
0
Merge Requests
0
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
16869b02
Commit
16869b02
authored
Jan 05, 2025
by
Jonathan Thomas
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'hidden-icon' into 'develop'
Custom Player Icons See merge request
!24
parents
237fd0be
0ea724d9
Pipeline
#13263
passed with stages
in 2 minutes 10 seconds
Changes
12
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
265 additions
and
44 deletions
+265
-44
CHANGELOG.md
CHANGELOG.md
+6
-6
gradle.properties
gradle.properties
+2
-1
MixinPlayerCustomTexture.java
.../com/owlmaddie/mixin/client/MixinPlayerCustomTexture.java
+28
-0
MixinPlayerSkinTexture.java
...va/com/owlmaddie/mixin/client/MixinPlayerSkinTexture.java
+52
-0
IPlayerSkinTexture.java
src/client/java/com/owlmaddie/skin/IPlayerSkinTexture.java
+13
-0
PlayerCustomTexture.java
src/client/java/com/owlmaddie/skin/PlayerCustomTexture.java
+11
-0
SkinUtils.java
src/client/java/com/owlmaddie/skin/SkinUtils.java
+38
-0
BubbleRenderer.java
src/client/java/com/owlmaddie/ui/BubbleRenderer.java
+78
-35
ChatScreen.java
src/client/java/com/owlmaddie/ui/ChatScreen.java
+11
-0
VersionUtils.java
src/client/java/com/owlmaddie/utils/VersionUtils.java
+14
-0
creaturechat.client.mixins.json
src/client/resources/creaturechat.client.mixins.json
+11
-1
fabric.mod.json
src/main/resources/fabric.mod.json
+1
-1
No files found.
CHANGELOG.md
View file @
16869b02
...
...
@@ -7,14 +7,14 @@ All notable changes to **CreatureChat** are documented in this file. The format
## Unreleased
### Added
-
Rate limiter for LLM unit tests (to prevent rate limit issues from certain providers when running all tests)
-
Check friendship direction (+ or -) in LLM unit tests (to verify friendship is output correctly)
### Changed
-
Improved LLM unit tests to check for both a positive and negative behaviors (i.e. FOLLOW and not LEAD, ATTACK and not FLEE, etc...)
-
Player Icons (custom art embedded in player skin)
-
New mixin to extend PlayerSkinTexture to make a copy of the NativeImage + pixel toggle to enable
-
Improved LLM Unit tests (to prevent rate limit issues from certain providers when running all tests)
-
Check friendship direction (+ or -) in LLM unit tests (to verify friendship direction is output correctly)
### Fixed
-
Changing death message timestamp output to use DEBUG log level
-
Fixed death messages for mobs with no chat data
-
Fixed transparent background behind chat screen for Minecraft 1.20 and 1.20.1.
## [1.2.1] - 2025-01-01
...
...
gradle.properties
View file @
16869b02
...
...
@@ -11,7 +11,7 @@ archives_base_name=creaturechat
# check these on https://fabricmc.net/develop
minecraft_version
=
1.20.4
yarn_mappings
=
1.20.4+build.3
loader_version
=
0.15.1
0
loader_version
=
0.15.1
1
#Fabric api
fabric_version
=
0.97.0+1.20.4
\ No newline at end of file
src/client/java/com/owlmaddie/mixin/client/MixinPlayerCustomTexture.java
0 → 100644
View file @
16869b02
package
com
.
owlmaddie
.
mixin
.
client
;
import
com.owlmaddie.skin.PlayerCustomTexture
;
import
com.owlmaddie.skin.SkinUtils
;
import
net.minecraft.util.Identifier
;
import
org.spongepowered.asm.mixin.Mixin
;
import
org.spongepowered.asm.mixin.Overwrite
;
/**
* A Mixin for PlayerCustomTexture to implement hasCustomIcon using SkinUtils.
*/
@Mixin
(
PlayerCustomTexture
.
class
)
public
abstract
class
MixinPlayerCustomTexture
{
/**
* Overwrites the default implementation of hasCustomIcon to provide custom skin support.
*
* @param skinId the Identifier of the skin
* @return true if the skin has a custom icon; false otherwise
*
* @reason Add functionality to determine custom icons based on SkinUtils logic.
* @author jonoomph
*/
@Overwrite
public
static
boolean
hasCustomIcon
(
Identifier
skinId
)
{
// Delegate to SkinUtils to check for custom skin properties
return
SkinUtils
.
checkCustomSkinKey
(
skinId
);
}
}
src/client/java/com/owlmaddie/mixin/client/MixinPlayerSkinTexture.java
0 → 100644
View file @
16869b02
package
com
.
owlmaddie
.
mixin
.
client
;
import
com.owlmaddie.skin.IPlayerSkinTexture
;
import
net.minecraft.client.texture.NativeImage
;
import
net.minecraft.client.texture.PlayerSkinTexture
;
import
net.minecraft.client.texture.ResourceTexture
;
import
net.minecraft.util.Identifier
;
import
org.spongepowered.asm.mixin.Mixin
;
import
org.spongepowered.asm.mixin.Unique
;
import
org.spongepowered.asm.mixin.injection.At
;
import
org.spongepowered.asm.mixin.injection.Inject
;
import
org.spongepowered.asm.mixin.injection.callback.CallbackInfo
;
/**
* The {@code MixinPlayerSkinTexture} class injects code into the PlayerSkinTexture class, to make a copy
* of the player's skin native image, so we can later use it for pixel checking (black/white key) for
* loading custom player icons in the unused UV coordinates of the player skin image.
*/
@Mixin
(
PlayerSkinTexture
.
class
)
public
abstract
class
MixinPlayerSkinTexture
extends
ResourceTexture
implements
IPlayerSkinTexture
{
@Unique
private
NativeImage
cachedSkinImage
;
public
MixinPlayerSkinTexture
(
Identifier
location
)
{
super
(
location
);
}
@Inject
(
method
=
"onTextureLoaded"
,
at
=
@At
(
"HEAD"
))
private
void
captureNativeImage
(
NativeImage
image
,
CallbackInfo
ci
)
{
// Instead of image.copy(), we do a manual clone
this
.
cachedSkinImage
=
cloneNativeImage
(
image
);
}
@Override
public
NativeImage
getLoadedImage
()
{
return
this
.
cachedSkinImage
;
}
// Example of the utility method in the same class (or in a separate helper):
private
static
NativeImage
cloneNativeImage
(
NativeImage
source
)
{
NativeImage
copy
=
new
NativeImage
(
source
.
getFormat
(),
source
.
getWidth
(),
source
.
getHeight
(),
false
);
copy
.
copyFrom
(
source
);
return
copy
;
}
}
src/client/java/com/owlmaddie/skin/IPlayerSkinTexture.java
0 → 100644
View file @
16869b02
package
com
.
owlmaddie
.
skin
;
import
net.minecraft.client.texture.NativeImage
;
import
org.jetbrains.annotations.Nullable
;
/**
* The {@code IPlayerSkinTexture} interface adds a new getLoadedImage method to PlayerSkinTexture instances
*/
public
interface
IPlayerSkinTexture
{
@Nullable
NativeImage
getLoadedImage
();
}
\ No newline at end of file
src/client/java/com/owlmaddie/skin/PlayerCustomTexture.java
0 → 100644
View file @
16869b02
package
com
.
owlmaddie
.
skin
;
import
net.minecraft.util.Identifier
;
public
class
PlayerCustomTexture
{
public
static
boolean
hasCustomIcon
(
Identifier
skinId
)
{
// By default, do nothing special
return
false
;
}
}
src/client/java/com/owlmaddie/skin/SkinUtils.java
0 → 100644
View file @
16869b02
package
com
.
owlmaddie
.
skin
;
import
net.minecraft.client.MinecraftClient
;
import
net.minecraft.client.texture.AbstractTexture
;
import
net.minecraft.client.texture.NativeImage
;
import
net.minecraft.util.Identifier
;
/**
* SkinUtils contains functions to check for certain black and white pixel values in a skin, to determine
* if the skin contains a custom hidden icon to show in the player chat message.
*/
public
class
SkinUtils
{
public
static
boolean
checkCustomSkinKey
(
Identifier
skinId
)
{
// Grab the AbstractTexture from the TextureManager
AbstractTexture
tex
=
MinecraftClient
.
getInstance
().
getTextureManager
().
getTexture
(
skinId
);
// Check if it implements our Mixin interface: IPlayerSkinTexture
if
(
tex
instanceof
IPlayerSkinTexture
iSkin
)
{
// Get the NativeImage we stored in the Mixin
NativeImage
image
=
iSkin
.
getLoadedImage
();
if
(
image
!=
null
)
{
int
width
=
image
.
getWidth
();
int
height
=
image
.
getHeight
();
// Check we have the full 64x64
if
(
width
==
64
&&
height
==
64
)
{
// Example: black & white pixel at (31,48) and (32,48)
int
color31_48
=
image
.
getColor
(
31
,
49
);
int
color32_48
=
image
.
getColor
(
32
,
49
);
return
(
color31_48
==
0xFF000000
&&
color32_48
==
0xFFFFFFFF
);
}
}
}
// If it's still loading, or not a PlayerSkinTexture, or no NativeImage loaded yet
return
false
;
}
}
src/client/java/com/owlmaddie/ui/BubbleRenderer.java
View file @
16869b02
...
...
@@ -4,9 +4,8 @@ import com.mojang.blaze3d.systems.RenderSystem;
import
com.owlmaddie.chat.ChatDataManager
;
import
com.owlmaddie.chat.EntityChatData
;
import
com.owlmaddie.chat.PlayerData
;
import
com.owlmaddie.utils.EntityHeights
;
import
com.owlmaddie.utils.EntityRendererAccessor
;
import
com.owlmaddie.utils.TextureLoader
;
import
com.owlmaddie.skin.PlayerCustomTexture
;
import
com.owlmaddie.utils.*
;
import
net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext
;
import
net.minecraft.client.MinecraftClient
;
import
net.minecraft.client.font.TextRenderer
;
...
...
@@ -228,6 +227,9 @@ public class BubbleRenderer {
EntityRenderer
renderer
=
EntityRendererAccessor
.
getEntityRenderer
(
entity
);
Identifier
playerTexture
=
renderer
.
getTexture
(
entity
);
// Check for black and white pixels (using the Mixin-based check)
boolean
customSkinFound
=
PlayerCustomTexture
.
hasCustomIcon
(
playerTexture
);
// Set shader & texture
RenderSystem
.
setShader
(
GameRenderer:
:
getPositionColorTexLightmapProgram
);
RenderSystem
.
setShaderTexture
(
0
,
playerTexture
);
...
...
@@ -241,42 +243,83 @@ public class BubbleRenderer {
// Prepare the tessellator and buffer
Tessellator
tessellator
=
Tessellator
.
getInstance
();
BufferBuilder
bufferBuilder
=
tessellator
.
getBuffer
();
// Get the current matrix position
Matrix4f
matrix4f
=
matrices
.
peek
().
getPositionMatrix
();
// Begin drawing quads with the correct vertex format
bufferBuilder
.
begin
(
VertexFormat
.
DrawMode
.
QUADS
,
VertexFormats
.
POSITION_COLOR_TEXTURE_LIGHT
);
// Texture coordinates for the face region (8, 8) to (16, 16) in a 64x64 texture
float
textureWidth
=
64.0
F
;
float
textureHeight
=
64.0
F
;
float
u1
=
8.0
F
/
textureWidth
;
float
v1
=
8.0
F
/
textureHeight
;
float
u2
=
16.0
F
/
textureWidth
;
float
v2
=
16.0
F
/
textureHeight
;
Matrix4f
matrix4f
=
matrices
.
peek
().
getPositionMatrix
();
float
z
=
-
0.01
F
;
// Draw face
bufferBuilder
.
vertex
(
matrix4f
,
x
,
y
+
height
,
z
).
color
(
255
,
255
,
255
,
255
).
texture
(
u1
,
v2
).
light
(
light
).
overlay
(
overlay
).
next
();
// bottom left
bufferBuilder
.
vertex
(
matrix4f
,
x
+
width
,
y
+
height
,
z
).
color
(
255
,
255
,
255
,
255
).
texture
(
u2
,
v2
).
light
(
light
).
overlay
(
overlay
).
next
();
// bottom right
bufferBuilder
.
vertex
(
matrix4f
,
x
+
width
,
y
,
z
).
color
(
255
,
255
,
255
,
255
).
texture
(
u2
,
v1
).
light
(
light
).
overlay
(
overlay
).
next
();
// top right
bufferBuilder
.
vertex
(
matrix4f
,
x
,
y
,
z
).
color
(
255
,
255
,
255
,
255
).
texture
(
u1
,
v1
).
light
(
light
).
overlay
(
overlay
).
next
();
// top left
// Coordinates for the hat (overlay)
float
hatU1
=
40.0
F
/
textureWidth
;
float
hatV1
=
8.0
F
/
textureHeight
;
float
hatU2
=
48.0
F
/
textureWidth
;
float
hatV2
=
16.0
F
/
textureHeight
;
// Adjust depth for hat layer
z
-=
0.01
F
;
// Draw hat (overlay)
bufferBuilder
.
vertex
(
matrix4f
,
x
,
y
+
height
,
z
).
color
(
255
,
255
,
255
,
255
).
texture
(
hatU1
,
hatV2
).
light
(
light
).
overlay
(
overlay
).
next
();
bufferBuilder
.
vertex
(
matrix4f
,
x
+
width
,
y
+
height
,
z
).
color
(
255
,
255
,
255
,
255
).
texture
(
hatU2
,
hatV2
).
light
(
light
).
overlay
(
overlay
).
next
();
bufferBuilder
.
vertex
(
matrix4f
,
x
+
width
,
y
,
z
).
color
(
255
,
255
,
255
,
255
).
texture
(
hatU2
,
hatV1
).
light
(
light
).
overlay
(
overlay
).
next
();
bufferBuilder
.
vertex
(
matrix4f
,
x
,
y
,
z
).
color
(
255
,
255
,
255
,
255
).
texture
(
hatU1
,
hatV1
).
light
(
light
).
overlay
(
overlay
).
next
();
if
(
customSkinFound
)
{
// Hidden icon UV coordinates
float
[][]
newCoordinates
=
{
{
0.0
F
,
0.0
F
,
8.0
F
,
8.0
F
,
0
F
,
0
F
},
// Row 1 left
{
24.0
F
,
0.0
F
,
32.0
F
,
8.0
F
,
8
F
,
0
F
},
// Row 1 middle
{
32.0
F
,
0.0
F
,
40.0
F
,
8.0
F
,
16
F
,
0
F
},
// Row 1 right
{
56.0
F
,
0.0
F
,
64.0
F
,
8.0
F
,
0
F
,
8
F
},
// Row 2 left
{
56.0
F
,
20.0
F
,
64.0
F
,
28.0
F
,
8
F
,
8
F
},
// Row 2 middle
{
36.0
F
,
16.0
F
,
44.0
F
,
20.0
F
,
16
F
,
8
F
},
// Row 2 right top
{
56.0
F
,
16.0
F
,
64.0
F
,
20.0
F
,
16
F
,
12
F
},
// Row 2 right bottom
{
56.0
F
,
28.0
F
,
64.0
F
,
36.0
F
,
0
F
,
16
F
},
// Row 3 left
{
56.0
F
,
36.0
F
,
64.0
F
,
44.0
F
,
8
F
,
16
F
},
// Row 3 middle
{
56.0
F
,
44.0
F
,
64.0
F
,
52.0
F
,
16
F
,
16
F
},
// Row 3 right
};
float
scaleFactor
=
0.77
F
;
for
(
float
[]
coords
:
newCoordinates
)
{
float
newU1
=
coords
[
0
]
/
64.0
F
;
float
newV1
=
coords
[
1
]
/
64.0
F
;
float
newU2
=
coords
[
2
]
/
64.0
F
;
float
newV2
=
coords
[
3
]
/
64.0
F
;
float
offsetX
=
coords
[
4
]
*
scaleFactor
;
float
offsetY
=
coords
[
5
]
*
scaleFactor
;
float
scaledX
=
x
+
offsetX
;
float
scaledY
=
y
+
offsetY
;
float
scaledWidth
=
(
coords
[
2
]
-
coords
[
0
])
*
scaleFactor
;
float
scaledHeight
=
(
coords
[
3
]
-
coords
[
1
])
*
scaleFactor
;
bufferBuilder
.
vertex
(
matrix4f
,
scaledX
,
scaledY
+
scaledHeight
,
z
)
.
color
(
255
,
255
,
255
,
255
).
texture
(
newU1
,
newV2
).
light
(
light
).
overlay
(
overlay
).
next
();
bufferBuilder
.
vertex
(
matrix4f
,
scaledX
+
scaledWidth
,
scaledY
+
scaledHeight
,
z
)
.
color
(
255
,
255
,
255
,
255
).
texture
(
newU2
,
newV2
).
light
(
light
).
overlay
(
overlay
).
next
();
bufferBuilder
.
vertex
(
matrix4f
,
scaledX
+
scaledWidth
,
scaledY
,
z
)
.
color
(
255
,
255
,
255
,
255
).
texture
(
newU2
,
newV1
).
light
(
light
).
overlay
(
overlay
).
next
();
bufferBuilder
.
vertex
(
matrix4f
,
scaledX
,
scaledY
,
z
)
.
color
(
255
,
255
,
255
,
255
).
texture
(
newU1
,
newV1
).
light
(
light
).
overlay
(
overlay
).
next
();
}
}
else
{
// Normal face coordinates
float
u1
=
8.0
F
/
64.0
F
;
float
v1
=
8.0
F
/
64.0
F
;
float
u2
=
16.0
F
/
64.0
F
;
float
v2
=
16.0
F
/
64.0
F
;
bufferBuilder
.
vertex
(
matrix4f
,
x
,
y
+
height
,
z
)
.
color
(
255
,
255
,
255
,
255
).
texture
(
u1
,
v2
).
light
(
light
).
overlay
(
overlay
).
next
();
bufferBuilder
.
vertex
(
matrix4f
,
x
+
width
,
y
+
height
,
z
)
.
color
(
255
,
255
,
255
,
255
).
texture
(
u2
,
v2
).
light
(
light
).
overlay
(
overlay
).
next
();
bufferBuilder
.
vertex
(
matrix4f
,
x
+
width
,
y
,
z
)
.
color
(
255
,
255
,
255
,
255
).
texture
(
u2
,
v1
).
light
(
light
).
overlay
(
overlay
).
next
();
bufferBuilder
.
vertex
(
matrix4f
,
x
,
y
,
z
)
.
color
(
255
,
255
,
255
,
255
).
texture
(
u1
,
v1
).
light
(
light
).
overlay
(
overlay
).
next
();
// Hat layer
float
hatU1
=
40.0
F
/
64.0
F
;
float
hatV1
=
8.0
F
/
64.0
F
;
float
hatU2
=
48.0
F
/
64.0
F
;
float
hatV2
=
16.0
F
/
64.0
F
;
z
-=
0.01
F
;
bufferBuilder
.
vertex
(
matrix4f
,
x
,
y
+
height
,
z
)
.
color
(
255
,
255
,
255
,
255
).
texture
(
hatU1
,
hatV2
).
light
(
light
).
overlay
(
overlay
).
next
();
bufferBuilder
.
vertex
(
matrix4f
,
x
+
width
,
y
+
height
,
z
)
.
color
(
255
,
255
,
255
,
255
).
texture
(
hatU2
,
hatV2
).
light
(
light
).
overlay
(
overlay
).
next
();
bufferBuilder
.
vertex
(
matrix4f
,
x
+
width
,
y
,
z
)
.
color
(
255
,
255
,
255
,
255
).
texture
(
hatU2
,
hatV1
).
light
(
light
).
overlay
(
overlay
).
next
();
bufferBuilder
.
vertex
(
matrix4f
,
x
,
y
,
z
)
.
color
(
255
,
255
,
255
,
255
).
texture
(
hatU1
,
hatV1
).
light
(
light
).
overlay
(
overlay
).
next
();
}
tessellator
.
draw
();
// Disable blending and depth test
...
...
src/client/java/com/owlmaddie/ui/ChatScreen.java
View file @
16869b02
...
...
@@ -2,6 +2,7 @@ package com.owlmaddie.ui;
import
com.owlmaddie.chat.ChatDataManager
;
import
com.owlmaddie.network.ClientPackets
;
import
com.owlmaddie.utils.VersionUtils
;
import
net.minecraft.client.gui.DrawContext
;
import
net.minecraft.client.gui.screen.Screen
;
import
net.minecraft.client.gui.widget.ButtonWidget
;
...
...
@@ -99,6 +100,11 @@ public class ChatScreen extends Screen {
@Override
public
void
render
(
DrawContext
context
,
int
mouseX
,
int
mouseY
,
float
delta
)
{
// Render custom background only for older versions
if
(
VersionUtils
.
isOlderThan
(
"1.20.2"
))
{
renderBackground
(
context
);
}
// Render the label text above the text field
int
labelWidth
=
textRenderer
.
getWidth
(
labelText
);
int
labelX
=
(
this
.
width
-
labelWidth
)
/
2
;
// Centered X position
...
...
@@ -116,6 +122,11 @@ public class ChatScreen extends Screen {
super
.
render
(
context
,
mouseX
,
mouseY
,
delta
);
}
public
void
renderBackground
(
DrawContext
context
)
{
// Draw a slightly lighter semi-transparent rectangle as the background
context
.
fillGradient
(
0
,
0
,
this
.
width
,
this
.
height
,
0xA3000000
,
0xA3000000
);
}
@Override
public
boolean
shouldCloseOnEsc
()
{
// Return true if you want the screen to close when the ESC key is pressed
...
...
src/client/java/com/owlmaddie/utils/VersionUtils.java
0 → 100644
View file @
16869b02
package
com
.
owlmaddie
.
utils
;
import
net.minecraft.SharedConstants
;
/**
* The {@code VersionUtils} class is used to quickly compare the current version of Minecraft.
*/
public
class
VersionUtils
{
public
static
boolean
isOlderThan
(
String
targetVersion
)
{
String
currentVersion
=
SharedConstants
.
getGameVersion
().
getName
();
return
currentVersion
.
compareTo
(
targetVersion
)
<
0
;
}
}
\ No newline at end of file
src/client/resources/creaturechat.client.mixins.json
View file @
16869b02
...
...
@@ -3,8 +3,18 @@
"package"
:
"com.owlmaddie.mixin.client"
,
"compatibilityLevel"
:
"JAVA_17"
,
"client"
:
[
"EntityRendererMixin"
"EntityRendererMixin"
,
"MixinPlayerCustomTexture"
,
"MixinPlayerSkinTexture"
],
"env"
:
{
"client.MixinPlayerCustomTexture"
:
{
"minVersion"
:
"1.20"
},
"client.MixinPlayerSkinTexture"
:
{
"minVersion"
:
"1.20"
}
},
"injectors"
:
{
"defaultRequire"
:
1
}
...
...
src/main/resources/fabric.mod.json
View file @
16869b02
...
...
@@ -33,7 +33,7 @@
"accessWidener"
:
"creaturechat.accesswidener"
,
"depends"
:
{
"fabricloader"
:
">=0.14.22"
,
"minecraft"
:
"~1.20.
2
"
,
"minecraft"
:
"~1.20.
4
"
,
"java"
:
">=17"
,
"fabric-api"
:
"*"
}
...
...
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