Skip to content

Commit 5f960e6

Browse files
Updating method using packet session write
1 parent 4b113a3 commit 5f960e6

File tree

3 files changed

+138
-31
lines changed

3 files changed

+138
-31
lines changed

go.mod

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,23 @@ module github.com/redstonecraftgg/df-noteblockplayer
22

33
go 1.24.5
44

5-
require github.com/df-mc/dragonfly v0.10.8
5+
require (
6+
github.com/df-mc/dragonfly v0.10.8
7+
github.com/go-gl/mathgl v1.2.0
8+
github.com/sandertv/gophertunnel v1.50.0
9+
)
610

711
require (
812
github.com/brentp/intintmap v0.0.0-20190211203843-30dc0ade9af9 // indirect
913
github.com/cespare/xxhash/v2 v2.3.0 // indirect
1014
github.com/df-mc/goleveldb v1.1.9 // indirect
1115
github.com/df-mc/jsonc v1.0.5 // indirect
1216
github.com/df-mc/worldupgrader v1.0.20 // indirect
13-
github.com/go-gl/mathgl v1.2.0 // indirect
1417
github.com/go-jose/go-jose/v4 v4.1.0 // indirect
1518
github.com/golang/snappy v0.0.4 // indirect
1619
github.com/google/uuid v1.6.0 // indirect
1720
github.com/klauspost/compress v1.17.11 // indirect
1821
github.com/sandertv/go-raknet v1.14.3-0.20250305181847-6af3e95113d6 // indirect
19-
github.com/sandertv/gophertunnel v1.50.0 // indirect
2022
github.com/segmentio/fasthash v1.0.3 // indirect
2123
golang.org/x/exp v0.0.0-20250103183323-7d7fa50e5329 // indirect
2224
golang.org/x/net v0.35.0 // indirect

noteblockplayer.go

Lines changed: 69 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -194,45 +194,86 @@ func playSong(eh *world.EntityHandle, song *Song) {
194194
}
195195
if notes, found := notesPerTick[tick]; found {
196196
for _, note := range notes {
197-
inst := sound.Piano()
198-
if note.Instrument >= 0 && note.Instrument < len(instrumentSounds) {
199-
inst = instrumentSounds[note.Instrument]
200-
}
201-
pitch := pitchKey(note.Key)
202-
// For further enhancement: use velocity, custom pitch, and panning as needed.
203-
/* fmt.Printf(
204-
"Tick=%d Layer=%d Instr=%d Key=%d Pitch=%d Vel=%d Pan=%d\n",
205-
note.Tick, note.Layer, note.Instrument, note.Key, pitch, note.Velocity, note.Panning,
206-
) */
207-
playSoundSelf(eh, sound.Note{
208-
Instrument: inst,
209-
Pitch: pitch,
197+
_ = eh.ExecWorld(func(tx *world.Tx, ent world.Entity) {
198+
pp, ok := ent.(*player.Player)
199+
if !ok {
200+
return
201+
}
202+
pos := pp.Position()
203+
instrument := "note.harp"
204+
if note.Instrument >= 0 && note.Instrument < len(instrumentSounds) {
205+
switch note.Instrument {
206+
case 1:
207+
instrument = "note.basedrum"
208+
case 2:
209+
instrument = "note.snare"
210+
case 3:
211+
instrument = "note.hat"
212+
case 4:
213+
instrument = "note.bass"
214+
case 5:
215+
instrument = "note.flute"
216+
case 6:
217+
instrument = "note.bell"
218+
case 7:
219+
instrument = "note.guitar"
220+
case 8:
221+
instrument = "note.chime"
222+
case 9:
223+
instrument = "note.xylophone"
224+
case 10:
225+
instrument = "note.iron_xylophone"
226+
case 11:
227+
instrument = "note.cow_bell"
228+
case 12:
229+
instrument = "note.didgeridoo"
230+
case 13:
231+
instrument = "note.bit"
232+
case 14:
233+
instrument = "note.banjo"
234+
case 15:
235+
instrument = "note.pling"
236+
}
237+
}
238+
pitch := Floatkey(note.Key)
239+
volume := FloatVel(note.Velocity)
240+
PacketPlaySound(pp, instrument, pitch, volume, pos)
210241
})
211242
}
212243
}
213244
}
214245
}
215246

216-
// playSoundSelf plays a sound only for the provided player entity (self).
217-
func playSoundSelf(eh *world.EntityHandle, snd world.Sound) {
218-
_ = eh.ExecWorld(func(tx *world.Tx, ent world.Entity) {
219-
pp, ok := ent.(*player.Player)
220-
if !ok {
221-
return
222-
}
223-
pos := pp.Position()
224-
tx.PlaySound(pos, snd)
225-
})
226-
}
227-
228-
// pitchKey calculates the Bedrock note pitch index based on the NBS note key.
247+
// PitchKey calculates the Bedrock note pitch index based on the NBS note key.
229248
// Bedrock's base is 33 (F#3).
230-
func pitchKey(key int) int {
249+
func PitchKey(key int) int {
231250
base := 33 // F#3 is key 33 in Bedrock
232251
return key - base
233252
}
234253

235-
// Pow is a helper to call math.Pow.
254+
// Bedrock "note" starts at key 33 (F#3). Each +12 is one octave (double freq/float).
255+
//
256+
// F#3 = 0.5, F#4 = 1.0, F#5 = 2.0, etc.
257+
//
258+
// So, the formulat is: 0.5 * 2^((key-33)/12)
259+
func Floatkey(key int) float32 {
260+
baseKey := 33
261+
return float32(0.5 * math.Pow(2, float64(key-baseKey)/12))
262+
}
263+
264+
// FloatVel converts NBS/JSON note velocity (0-100) to Bedrock/Dragonfly volume [0.0, 1.0].
265+
// Values below or equal 0 are muted; above 100 are clamped to 1.0
266+
func FloatVel(val int) float32 {
267+
if val <= 0 {
268+
return 0
269+
}
270+
if val >= 100 {
271+
return 1.0
272+
}
273+
return float32(val) / 100.0
274+
}
275+
276+
// Pow is a helper function alias for math.Pow (for convenience).
236277
func Pow(base, exp float64) float64 {
237278
return math.Pow(base, exp)
238279
}

packetsession.go

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package noteblockplayer
2+
3+
import (
4+
"reflect"
5+
"unsafe"
6+
7+
"github.com/df-mc/dragonfly/server/player"
8+
"github.com/go-gl/mathgl/mgl64"
9+
"github.com/sandertv/gophertunnel/minecraft/protocol/packet"
10+
)
11+
12+
// PacketPlaySound sends a PlaySound packet directly to the player's session connection.
13+
//
14+
// This function takes a player pointer (p), the sound name (name), float32 pitch and volume,
15+
// and a 3D position (pos, mgl64.Vec3). It first converts the position to [3]float32 as required
16+
// by the network packet.
17+
//
18+
// Then, using Go reflection and pointer-unsafe tricks, it accesses the unexported player session
19+
// field "s" and attempts to invoke the "WritePacket" method on it. If not directly available, it
20+
// tries to extract the connection object (field "conn") and invoke "WritePacket" on that.
21+
//
22+
// Ultimately, this method delivers the PlaySound packet to the player, which makes the sound
23+
// play at the specified position with the given pitch and volume from the server side.
24+
// This bypasses higher level APIs and directly calls the underlying session, which is
25+
// useful for custom, low-level sound triggers in plugins or game logic.
26+
func PacketPlaySound(p *player.Player, name string, pitch, volume float32, pos mgl64.Vec3) {
27+
mgl32Pos := [3]float32{float32(pos[0]), float32(pos[1]), float32(pos[2])}
28+
29+
val := reflect.ValueOf(p).Elem().FieldByName("s")
30+
if !val.IsValid() {
31+
return
32+
}
33+
34+
sessionPtr := reflect.NewAt(val.Type(), unsafe.Pointer(val.UnsafeAddr())).Elem()
35+
36+
method := sessionPtr.MethodByName("WritePacket")
37+
if method.IsValid() {
38+
method.Call([]reflect.Value{
39+
reflect.ValueOf(&packet.PlaySound{
40+
SoundName: name,
41+
Volume: volume, // float32
42+
Pitch: pitch, // float32
43+
Position: mgl32Pos,
44+
}),
45+
})
46+
return
47+
}
48+
49+
connField := sessionPtr.Elem().FieldByName("conn")
50+
if connField.IsValid() {
51+
conn := reflect.NewAt(connField.Type(), unsafe.Pointer(connField.UnsafeAddr())).Elem()
52+
writeMethod := conn.MethodByName("WritePacket")
53+
if writeMethod.IsValid() {
54+
writeMethod.Call([]reflect.Value{
55+
reflect.ValueOf(&packet.PlaySound{
56+
SoundName: name,
57+
Volume: volume, // float32
58+
Pitch: pitch, // float32
59+
Position: mgl32Pos,
60+
}),
61+
})
62+
}
63+
}
64+
}

0 commit comments

Comments
 (0)