Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
1d8d26e
Merge pull request #1 from df-mc/master
Studgi Sep 17, 2025
d81fdda
Refactor entity riding system and update dependencies
Studgi Sep 18, 2025
8b1a904
Standardize vector types in riding system
Studgi Sep 19, 2025
70ab4cf
Downgrade to go 1.24.7
Studgi Sep 19, 2025
d429544
revert go version and modules bump
Studgi Sep 19, 2025
0fe9113
Refactor entity riding system with seat-based approach
Studgi Sep 19, 2025
5c75b15
Merge branch 'master' into master
Studgi Sep 19, 2025
00bb810
Add click position parameter to seat selection
Studgi Sep 20, 2025
52e4e0c
Merge remote-tracking branch 'origin/master'
Studgi Sep 20, 2025
cedc175
renamed rideable to riddenEntity
Studgi Sep 29, 2025
3035b8d
Merge branch 'master' into master
Studgi Sep 29, 2025
054406f
Merge pull request #2 from df-mc/master
Studgi Dec 20, 2025
7ec9b94
riding: add methods for managing multiple riders on riddenEntity inte…
Studgi Dec 21, 2025
baf8ea6
Merge remote-tracking branch 'origin/master'
Studgi Dec 21, 2025
54f14e2
riding: add comprehensive developer documentation for Rider and Ridea…
Studgi Dec 21, 2025
7329098
sleep: implement sleep functionality and related mechanics
Studgi Jan 7, 2026
b47e62b
entity_metadata: add support for baby entities in metadata flags
Studgi Jan 27, 2026
5ed826f
entity_metadata: fix flag setting for baby entities in metadata
Studgi Jan 27, 2026
209ae6d
entity_metadata: rename IsBaby method to Baby for consistency
Studgi Jan 31, 2026
7bff422
player: refactor riding entity handling to use world transactions
Studgi Jan 31, 2026
1091215
refactor interaction handlers to utilize world transactions
Studgi Jan 31, 2026
c30ef24
player: update RidingEntity method to return a boolean for entity exi…
Studgi Jan 31, 2026
2b078e5
player: update RidingEntity method to return a boolean for existence …
Studgi Jan 31, 2026
be1ec05
player: modify RidingEntity method to return nil for non-existent ent…
Studgi Jan 31, 2026
f7d8152
Merge pull request #3 from df-mc/master
Studgi Feb 15, 2026
976171d
remove unused baby interface from entity metadata
Studgi Feb 15, 2026
d057bce
Merge pull request #4 from df-mc/master
Studgi Mar 25, 2026
175ac1c
Merge pull request #5 from df-mc/master
Studgi Apr 6, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 12 additions & 13 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
module github.com/df-mc/dragonfly

go 1.24

toolchain go1.24.4
go 1.24.7

require (
github.com/brentp/intintmap v0.0.0-20190211203843-30dc0ade9af9
Expand All @@ -14,20 +12,21 @@ require (
github.com/pelletier/go-toml v1.9.5
github.com/sandertv/gophertunnel v1.49.0
github.com/segmentio/fasthash v1.0.3
golang.org/x/exp v0.0.0-20250103183323-7d7fa50e5329
golang.org/x/mod v0.22.0
golang.org/x/text v0.22.0
golang.org/x/tools v0.28.0
golang.org/x/exp v0.0.0-20250911091902-df9299821621
golang.org/x/mod v0.28.0
golang.org/x/text v0.29.0
golang.org/x/tools v0.37.0
)

require (
github.com/go-jose/go-jose/v4 v4.1.0 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/klauspost/compress v1.17.11 // indirect
github.com/go-jose/go-jose/v4 v4.1.2 // indirect
github.com/golang/snappy v1.0.0 // indirect
github.com/klauspost/compress v1.18.0 // indirect
github.com/muhammadmuzzammil1998/jsonc v1.0.0 // indirect
github.com/sandertv/go-raknet v1.14.3-0.20250305181847-6af3e95113d6 // indirect
golang.org/x/net v0.35.0 // indirect
golang.org/x/oauth2 v0.25.0 // indirect
golang.org/x/sync v0.11.0 // indirect
golang.org/x/crypto v0.42.0 // indirect
golang.org/x/net v0.44.0 // indirect
golang.org/x/oauth2 v0.31.0 // indirect
golang.org/x/sync v0.17.0 // indirect
gopkg.in/yaml.v2 v2.3.0 // indirect
)
71 changes: 45 additions & 26 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc=
cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
github.com/brentp/intintmap v0.0.0-20190211203843-30dc0ade9af9 h1:/G0ghZwrhou0Wq21qc1vXXMm/t/aKWkALWwITptKbE0=
github.com/brentp/intintmap v0.0.0-20190211203843-30dc0ade9af9/go.mod h1:TOk10ahXejq9wkEaym3KPRNeuR/h5Jx+s8QRWIa2oTM=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
Expand All @@ -8,24 +10,26 @@ github.com/df-mc/goleveldb v1.1.9 h1:ihdosZyy5jkQKrxucTQmN90jq/2lUwQnJZjIYIC/9YU
github.com/df-mc/goleveldb v1.1.9/go.mod h1:+NHCup03Sci5q84APIA21z3iPZCuk6m6ABtg4nANCSk=
github.com/df-mc/worldupgrader v1.0.19 h1:BnwRe/lVv9IhwRWZK5olvTYNTKWh7rhFdqNPDMnoKFM=
github.com/df-mc/worldupgrader v1.0.19/go.mod h1:tsSOLTRm9mpG7VHvYpAjjZrkRHWmSbKZAm9bOLNnlDk=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/go-gl/mathgl v1.2.0 h1:v2eOj/y1B2afDxF6URV1qCYmo1KW08lAMtTbOn3KXCY=
github.com/go-gl/mathgl v1.2.0/go.mod h1:pf9+b5J3LFP7iZ4XXaVzZrCle0Q/vNpB/vDe5+3ulRE=
github.com/go-jose/go-jose/v4 v4.1.0 h1:cYSYxd3pw5zd2FSXk2vGdn9igQU2PS8MuxrCOCl0FdY=
github.com/go-jose/go-jose/v4 v4.1.0/go.mod h1:GG/vqmYm3Von2nYiB2vGTXzdoNKE5tix5tuc6iAd+sw=
github.com/go-jose/go-jose/v4 v4.1.2 h1:TK/7NqRQZfgAh+Td8AlsrvtPoUyiHh0LqVvokh+1vHI=
github.com/go-jose/go-jose/v4 v4.1.2/go.mod h1:22cg9HWM1pOlnRiY+9cQYJ9XHmya1bYW8OeDM6Ku6Oo=
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=
github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
github.com/muhammadmuzzammil1998/jsonc v1.0.0 h1:8o5gBQn4ZA3NBA9DlTujCj2a4w0tqWrPVjDwhzkgTIs=
github.com/muhammadmuzzammil1998/jsonc v1.0.0/go.mod h1:saF2fIVw4banK0H4+/EuqfFLpRnoy5S+ECwTOCcRcSU=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
Expand All @@ -43,28 +47,43 @@ github.com/sandertv/gophertunnel v1.49.0 h1:9FIl4GGYMrFTmBPgkOTUskGWRQHIg4NMpY8J
github.com/sandertv/gophertunnel v1.49.0/go.mod h1:lmRarAmn25V/+QeiUbUDXeA26bEaNlX1wGEM/rj39ew=
github.com/segmentio/fasthash v1.0.3 h1:EI9+KE1EwvMLBWwjpRDc+fEM+prwxDYbslddQGtrmhM=
github.com/segmentio/fasthash v1.0.3/go.mod h1:waKX8l2N8yckOgmSsXJi7x1ZfdKZ4x7KRMzBtS3oedY=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
golang.org/x/exp v0.0.0-20250103183323-7d7fa50e5329 h1:9kj3STMvgqy3YA4VQXBrN7925ICMxD5wzMRcgA30588=
golang.org/x/exp v0.0.0-20250103183323-7d7fa50e5329/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c=
golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/yuin/goldmark v1.4.13 h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI=
golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8=
golang.org/x/exp v0.0.0-20250911091902-df9299821621 h1:2id6c1/gto0kaHYyrixvknJ8tUK/Qs5IsmBtrc+FtgU=
golang.org/x/exp v0.0.0-20250911091902-df9299821621/go.mod h1:TwQYMMnGpvZyc+JpB/UAuTNIsVJifOlSkrZkhcvpVUk=
golang.org/x/image v0.21.0 h1:c5qV36ajHpdj4Qi0GnE0jUc/yuo33OLFaa0d+crTD5s=
golang.org/x/image v0.21.0/go.mod h1:vUbsLavqK/W303ZroQQVKQ+Af3Yl6Uz1Ppu5J/cLz78=
golang.org/x/mod v0.28.0 h1:gQBtGhjxykdjY9YhZpSlZIsbnaE2+PgjfLWUQTnoZ1U=
golang.org/x/mod v0.28.0/go.mod h1:yfB/L0NOf/kmEbXjzCPOx1iK1fRutOydrCMsqRhEBxI=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
golang.org/x/oauth2 v0.25.0 h1:CY4y7XT9v0cRI9oupztF8AgiIu99L/ksR/Xp/6jrZ70=
golang.org/x/oauth2 v0.25.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I=
golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY=
golang.org/x/oauth2 v0.31.0 h1:8Fq0yVZLh4j4YA47vHKFTa9Ew5XIrCP8LC6UeNZnLxo=
golang.org/x/oauth2 v0.31.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/telemetry v0.0.0-20250908211612-aef8a434d053 h1:dHQOQddU4YHS5gY33/6klKjq7Gp3WwMyOXGNp5nzRj8=
golang.org/x/telemetry v0.0.0-20250908211612-aef8a434d053/go.mod h1:+nZKN+XVh4LCiA9DV3ywrzN4gumyCnKjau3NGb9SGoE=
golang.org/x/term v0.35.0 h1:bZBVKBudEyhRcajGcNc3jIfWPqV4y/Kt2XcoigOWtDQ=
golang.org/x/term v0.35.0/go.mod h1:TPGtkTLesOwf2DE8CgVYiZinHAOuy5AYUYT1lENIZnA=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
golang.org/x/tools v0.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8=
golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw=
golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk=
golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4=
golang.org/x/tools v0.37.0 h1:DVSRzp7FwePZW356yEAChSdNcQo6Nsp+fex1SUW09lE=
golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w=
golang.org/x/tools/go/expect v0.1.1-deprecated h1:jpBZDwmgPhXsKZC6WhL20P4b/wmnpsEAGHaNy0n/rJM=
golang.org/x/tools/go/expect v0.1.1-deprecated/go.mod h1:eihoPOH+FgIqa3FpoTwguz/bVUSGBlGQU67vpBeOrBY=
golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated h1:1h2MnaIAIXISqTFKdENegdpAgUXz6NrPEsbIeWaBRvM=
golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated/go.mod h1:RVAQXBGNv1ib0J382/DPCRS/BPnsGebyM1Gj5VSDpG8=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
Expand Down
26 changes: 26 additions & 0 deletions server/entity/riding.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package entity

import (
"github.com/df-mc/dragonfly/server/world"
"github.com/go-gl/mathgl/mgl32"
)

// Rider is an interface for entities that can ride other entities.
type Rider interface {
world.Entity
// RidingEntity returns the entity the player is currently riding.
RidingEntity() Rideable
// SeatPosition returns the position of where the player is sitting.
SeatPosition() mgl32.Vec3
// MountEntity mounts the Rider to an entity if the entity is Rideable and if there is a seat available.
MountEntity(e Rideable, position mgl32.Vec3, driver bool)
// DismountEntity dismounts the rider from the entity they are currently riding.
DismountEntity()
}

// Rideable is an interface for entities that can be ridden.
type Rideable interface {
world.Entity
Driver() Rider
Move(vector mgl32.Vec2, yaw, pitch float32)
}
10 changes: 10 additions & 0 deletions server/player/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ package player
import (
"github.com/df-mc/dragonfly/server/block/cube"
"github.com/df-mc/dragonfly/server/cmd"
"github.com/df-mc/dragonfly/server/entity"
"github.com/df-mc/dragonfly/server/event"
"github.com/df-mc/dragonfly/server/item"
"github.com/df-mc/dragonfly/server/player/skin"
"github.com/df-mc/dragonfly/server/session"
"github.com/df-mc/dragonfly/server/world"
"github.com/go-gl/mathgl/mgl32"
"github.com/go-gl/mathgl/mgl64"
"net"
"time"
Expand Down Expand Up @@ -138,6 +140,12 @@ type Handler interface {
// ctx.Cancel() may be called to prevent the player from dropping the entity.Item passed on the ground.
// e.Item() may be called to obtain the item stack dropped.
HandleItemDrop(ctx *Context, s item.Stack)
// HandleMount handles when a player mounts an entity. ctx.Cancel() may be called to cancel the player mounting
// an entity.
HandleMount(ctx *Context, r entity.Rideable, seatPos *mgl32.Vec3, driver *bool)
// HandleDismount handles when a player mounts an entity. ctx.Cancel() may be called to force the player
// to re-mount the entity.
HandleDismount(ctx *Context, r entity.Rideable)
// HandleTransfer handles a player being transferred to another server. ctx.Cancel() may be called to
// cancel the transfer.
HandleTransfer(ctx *Context, addr *net.UDPAddr)
Expand Down Expand Up @@ -188,6 +196,8 @@ func (NopHandler) HandleItemRelease(ctx *Context, item item.Stack, dur time.Dura
func (NopHandler) HandleItemConsume(*Context, item.Stack) {}
func (NopHandler) HandleItemDamage(*Context, item.Stack, int) {}
func (NopHandler) HandleAttackEntity(*Context, world.Entity, *float64, *float64, *bool) {}
func (NopHandler) HandleMount(*Context, entity.Rideable, *mgl32.Vec3, *bool) {}
func (NopHandler) HandleDismount(*Context, entity.Rideable) {}
func (NopHandler) HandleExperienceGain(*Context, *int) {}
func (NopHandler) HandlePunchAir(*Context) {}
func (NopHandler) HandleHurt(*Context, *float64, bool, *time.Duration, world.DamageSource) {}
Expand Down
55 changes: 55 additions & 0 deletions server/player/player.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"
"github.com/df-mc/dragonfly/server/player/debug"
"github.com/df-mc/dragonfly/server/player/hud"
"github.com/go-gl/mathgl/mgl32"
"math"
"math/rand/v2"
"net"
Expand Down Expand Up @@ -54,6 +55,9 @@ type playerData struct {
armour *inventory.Armour
heldSlot *uint32

rideable entity.Rideable
seatPosition mgl32.Vec3

sneaking, sprinting, swimming, gliding, crawling, flying,
invisible, immobile, onGround, usingItem bool
usingSince time.Time
Expand Down Expand Up @@ -2554,6 +2558,56 @@ func (p *Player) SetMaxAirSupply(duration time.Duration) {
p.updateState()
}

// RidingEntity returns the entity the player is currently riding.
func (p *Player) RidingEntity() entity.Rideable {
return p.rideable
}

// SeatPosition returns the position of where the player is sitting.
func (p *Player) SeatPosition() mgl32.Vec3 {
return p.seatPosition
}

// MountEntity mounts the player to an entity if the entity is rideable and if there is a seat available.
func (p *Player) MountEntity(r entity.Rideable, seatPos mgl32.Vec3, driver bool) {
ctx := event.C(p)
if p.h.HandleMount(ctx, r, &seatPos, &driver); ctx.Cancelled() {
return
}

if rd := p.RidingEntity(); rd != nil {
p.DismountEntity()
}

p.rideable = r
p.seatPosition = seatPos

p.updateState()
for _, v := range p.viewers() {
v.ViewEntityMount(p, r, driver)
}
}

// DismountEntity dismounts the player from an entity.
func (p *Player) DismountEntity() {
ctx := event.C(p)
r := p.RidingEntity()
if r == nil {
return
}
if p.h.HandleDismount(ctx, r); ctx.Cancelled() {
return
}

p.rideable = nil
p.seatPosition = mgl32.Vec3{}

p.updateState()
for _, v := range p.viewers() {
v.ViewEntityDismount(p, r)
}
}

// canBreathe returns true if the player can currently breathe.
func (p *Player) canBreathe() bool {
canTakeDamage := p.GameMode().AllowsTakingDamage()
Expand Down Expand Up @@ -2985,6 +3039,7 @@ func (p *Player) Close() error {
// close closes the player without disconnecting it. It executes code shared by both the closing and the
// disconnecting of players.
func (p *Player) close(msg string) {
p.DismountEntity()
// If the player is being disconnected while they are dead, we respawn the player
// so that the player logic works correctly the next time they join.
if p.Dead() && p.session() != nil {
Expand Down
2 changes: 2 additions & 0 deletions server/session/controllable.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package session
import (
"github.com/df-mc/dragonfly/server/block/cube"
"github.com/df-mc/dragonfly/server/cmd"
"github.com/df-mc/dragonfly/server/entity"
"github.com/df-mc/dragonfly/server/entity/effect"
"github.com/df-mc/dragonfly/server/item"
"github.com/df-mc/dragonfly/server/item/inventory"
Expand All @@ -25,6 +26,7 @@ type Controllable interface {
Name() string
world.Entity
item.User
entity.Rider
dialogue.Submitter
form.Submitter
cmd.Source
Expand Down
6 changes: 6 additions & 0 deletions server/session/entity_metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,12 @@ func (s *Session) addSpecificMetadata(e any, m protocol.EntityMetadata) {
if sc, ok := e.(scaled); ok {
m[protocol.EntityDataKeyScale] = float32(sc.Scale())
}
if r, ok := e.(entity.Rider); ok {
if rd := r.RidingEntity(); rd != nil {
m[protocol.EntityDataKeySeatOffset] = r.SeatPosition()
m.SetFlag(protocol.EntityDataKeyFlags, protocol.EntityDataFlagRiding)
}
}
if t, ok := e.(tnt); ok {
m[protocol.EntityDataKeyFuseTime] = int32(t.Fuse().Milliseconds() / 50)
m.SetFlag(protocol.EntityDataKeyFlags, protocol.EntityDataFlagIgnited)
Expand Down
2 changes: 2 additions & 0 deletions server/session/handler_interact.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ func (h *InteractHandler) Handle(p packet.Packet, s *Session, _ *world.Tx, c Con
int32(pos[2]),
},
})
case packet.InteractActionLeaveVehicle:
c.DismountEntity()
default:
return fmt.Errorf("unexpected interact packet action %v", pk.ActionType)
}
Expand Down
5 changes: 5 additions & 0 deletions server/session/handler_inventory_transaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ package session
import (
"fmt"
"github.com/df-mc/dragonfly/server/block/cube"
"github.com/df-mc/dragonfly/server/entity"
"github.com/df-mc/dragonfly/server/event"
"github.com/df-mc/dragonfly/server/item"
"github.com/df-mc/dragonfly/server/item/inventory"
"github.com/df-mc/dragonfly/server/world"
"github.com/go-gl/mathgl/mgl32"
"github.com/sandertv/gophertunnel/minecraft/protocol"
"github.com/sandertv/gophertunnel/minecraft/protocol/packet"
)
Expand Down Expand Up @@ -158,6 +160,9 @@ func (h *InventoryTransactionHandler) handleUseItemOnEntityTransaction(data *pro
var valid bool
switch data.ActionType {
case protocol.UseItemOnEntityActionInteract:
if r, ok := e.(entity.Rideable); ok {
c.MountEntity(r, mgl32.Vec3{}, true)
}
valid = c.UseItemOnEntity(e)
case protocol.UseItemOnEntityActionAttack:
valid = c.AttackEntity(e)
Expand Down
6 changes: 6 additions & 0 deletions server/session/handler_player_auth_input.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ func (h PlayerAuthInputHandler) handleMovement(pk *packet.PlayerAuthInput, s *Se
}
}

if rd := c.RidingEntity(); rd != nil {
if c == rd.Driver() {
rd.Move(pk.MoveVector, pk.Yaw, pk.Pitch)
}
}

pk.Position = pk.Position.Sub(mgl32.Vec3{0, 1.62}) // Sub the base offset of players from the pos.

newPos := vec32To64(pk.Position)
Expand Down
25 changes: 25 additions & 0 deletions server/session/world.go
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,31 @@ func (s *Session) ViewEntityTeleport(e world.Entity, position mgl64.Vec3) {
})
}

// ViewEntityMount ...
func (s *Session) ViewEntityMount(r world.Entity, rd world.Entity, driver bool) {
linkType := protocol.EntityLinkPassenger
if driver {
linkType = protocol.EntityLinkRider
}
s.writePacket(&packet.SetActorLink{EntityLink: protocol.EntityLink{
RiddenEntityUniqueID: int64(s.entityRuntimeID(rd)),
RiderEntityUniqueID: int64(s.entityRuntimeID(r)),
Type: byte(linkType),
RiderInitiated: true,
}})
}

// ViewEntityDismount ...
func (s *Session) ViewEntityDismount(r world.Entity, rd world.Entity) {
s.writePacket(&packet.SetActorLink{EntityLink: protocol.EntityLink{
RiddenEntityUniqueID: int64(s.entityRuntimeID(rd)),
RiderEntityUniqueID: int64(s.entityRuntimeID(r)),
Type: byte(protocol.EntityLinkRemove),
RiderInitiated: true,
Immediate: true,
}})
}

// ViewEntityItems ...
func (s *Session) ViewEntityItems(e world.Entity) {
runtimeID := s.entityRuntimeID(e)
Expand Down
Loading