Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implement crawling #936

Merged
merged 8 commits into from
Nov 17, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
23 changes: 23 additions & 0 deletions server/block/cube/bbox.go
Original file line number Diff line number Diff line change
Expand Up @@ -282,3 +282,26 @@ func (box BBox) ZOffset(nearby BBox, deltaZ float64) float64 {
}
return deltaZ
}

// Corners returns slice of all hitbox corners
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a full stop at the end of this doc and the other 2 in this file.

func (box BBox) Corners() []mgl64.Vec3 {
min := box.min
max := box.max
xyZ := mgl64.Vec3{min[0], min[1], max[2]}
xYz := mgl64.Vec3{min[0], max[1], min[2]}
xYZ := mgl64.Vec3{min[0], max[1], max[2]}
XYz := mgl64.Vec3{max[0], max[1], min[2]}
XyZ := mgl64.Vec3{max[0], min[1], max[2]}
Xyz := mgl64.Vec3{max[0], min[1], min[2]}
return []mgl64.Vec3{min, max, xyZ, xYz, xYZ, XYz, XyZ, Xyz}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd remove these variables and do something like this:

return []mgl64.Vec3{
  {min[0], min[1], max[2]},
  {min[0], max[1], min[2]},
  // etc.
}```

}

// Mul performs a scalar multiplication for min, max points of BBox
func (box BBox) Mul(val float64) BBox {
return BBox{min: box.min.Mul(val), max: box.max.Mul(val)}
}

// Volume calculates Volume of the BBox
func (box BBox) Volume() float64 {
return box.Height() * box.Length() * box.Width()
}
45 changes: 40 additions & 5 deletions server/player/player.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ type Player struct {
armour *inventory.Armour
heldSlot *atomic.Uint32

sneaking, sprinting, swimming, gliding, flying,
sneaking, sprinting, swimming, gliding, crawling, flying,
invisible, immobile, onGround, usingItem atomic.Bool
usingSince atomic.Int64

Expand Down Expand Up @@ -951,7 +951,7 @@ func (p *Player) Respawn() {
// particles show up under the feet. The player will only start sprinting if its food level is high enough.
// If the player is sneaking when calling StartSprinting, it is stopped from sneaking.
func (p *Player) StartSprinting() {
if !p.hunger.canSprint() && p.GameMode().AllowsTakingDamage() {
if !p.hunger.canSprint() && p.GameMode().AllowsTakingDamage() || p.crawling.Load() {
return
}
ctx := event.C()
Expand Down Expand Up @@ -1044,6 +1044,37 @@ func (p *Player) StopSwimming() {
p.updateState()
}

// StartCrawling makes the player start crawling if it is not currently doing so. If the player is sneaking
// while StartCrawling is called, the sneaking is stopped.
func (p *Player) StartCrawling() {
var isAir bool
for _, corner := range p.Type().BBox(p).Translate(p.Position()).Corners() {
_, isAir = p.World().Block(cube.PosFromVec3(corner).Add(cube.Pos{0, 1, 0})).(block.Air)
if !isAir {
break
}
}

if !isAir && !p.crawling.CompareAndSwap(false, true) {
return
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be slightly simplified to

for _, corner := range p.Type().BBox(p).Translate(p.Position()).Corners() {
	_, isAir = p.World().Block(cube.PosFromVec3(corner).Add(cube.Pos{0, 1, 0})).(block.Air)
	if !isAir {
		if !p.crawling.CompareAndSwap(false, true) {
			return
		}
		break
	}
}

p.StopSneaking()
p.updateState()
}

// StopCrawling makes the player stop crawling if it is currently doing so.
func (p *Player) StopCrawling() {
if !p.crawling.CompareAndSwap(true, false) {
return
}
p.updateState()
}

// Crawling checks if the player is currently crawling.
func (p *Player) Crawling() bool {
return p.crawling.Load()
}

// StartGliding makes the player start gliding if it is not currently doing so.
func (p *Player) StartGliding() {
if !p.gliding.CompareAndSwap(false, true) {
Expand Down Expand Up @@ -2615,12 +2646,16 @@ func (p *Player) OnGround() bool {
return p.onGround.Load()
}

// EyeHeight returns the eye height of the player: 1.62, or 0.52 if the player is swimming.
// EyeHeight returns the eye height of the player.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could keep the last part of the doc and say "0.52 if the player is crawling or swimming, 1.26 when sneaking or 1.62 when standing."

func (p *Player) EyeHeight() float64 {
if p.swimming.Load() {
switch {
case p.swimming.Load() || p.crawling.Load():
return 0.52
case p.sneaking.Load():
return 1.26
default:
return 1.62
}
return 1.62
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No point changing this to a default cause, keep how it was before

}

// PlaySound plays a world.Sound that only this Player can hear. Unlike World.PlaySound, it is not broadcast
Expand Down
4 changes: 2 additions & 2 deletions server/player/type.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ func (Type) BBox(e world.Entity) cube.BBox {
p := e.(*Player)
s := p.Scale()
switch {
case p.Gliding(), p.Swimming(), p.Crawling():
return cube.Box(-0.3*s, 0, -0.3*s, 0.3*s, 0.6*s, 0.3*s)
case p.Sneaking():
return cube.Box(-0.3*s, 0, -0.3*s, 0.3*s, 1.5*s, 0.3*s)
case p.Gliding(), p.Swimming():
return cube.Box(-0.3*s, 0, -0.3*s, 0.3*s, 0.6*s, 0.3*s)
default:
return cube.Box(-0.3*s, 0, -0.3*s, 0.3*s, 1.8*s, 0.3*s)
}
Expand Down
3 changes: 3 additions & 0 deletions server/session/controllable.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@ type Controllable interface {
StartSwimming()
Swimming() bool
StopSwimming()
StartCrawling()
Crawling() bool
StopCrawling()
StartFlying()
Flying() bool
StopFlying()
Expand Down
7 changes: 7 additions & 0 deletions server/session/entity_metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ func (s *Session) addSpecificMetadata(e any, m protocol.EntityMetadata) {
if sw, ok := e.(swimmer); ok && sw.Swimming() {
m.SetFlag(protocol.EntityDataKeyFlags, protocol.EntityDataFlagSwimming)
}
if cr, ok := e.(crawler); ok && cr.Crawling() {
m.SetFlag(protocol.EntityDataKeyFlags, protocol.EntityDataFlagCrawling)
}
if gl, ok := e.(glider); ok && gl.Gliding() {
m.SetFlag(protocol.EntityDataKeyFlags, protocol.EntityDataFlagGliding)
}
Expand Down Expand Up @@ -178,6 +181,10 @@ type swimmer interface {
Swimming() bool
}

type crawler interface {
Crawling() bool
}

type glider interface {
Gliding() bool
}
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 @@ -122,6 +122,12 @@ func (h PlayerAuthInputHandler) handleInputFlags(flags uint64, s *Session) {
if flags&packet.InputFlagStartJumping != 0 {
s.c.Jump()
}
if flags&packet.InputFlagStartCrawling != 0 {
s.c.StartCrawling()
}
if flags&packet.InputFlagStopCrawling != 0 {
s.c.StopCrawling()
}
if flags&packet.InputFlagMissedSwing != 0 {
s.swingingArm.Store(true)
defer s.swingingArm.Store(false)
Expand Down
Loading