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 entity liquid physics #653

Open
wants to merge 18 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
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
42 changes: 42 additions & 0 deletions server/block/liquid.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"github.com/df-mc/dragonfly/server/event"
"github.com/df-mc/dragonfly/server/item"
"github.com/df-mc/dragonfly/server/world"
"github.com/go-gl/mathgl/mgl64"
"math"
"sync"
)
Expand Down Expand Up @@ -33,6 +34,47 @@ func (s flowingWaterDisplacer) CanDisplace(b world.Liquid) bool {
return ok
}

// FlowVector returns the flow vector of the liquid at the given position.
func FlowVector(pos cube.Pos, w *world.World, l world.Liquid) (v mgl64.Vec3) {
var decay int
if !l.LiquidFalling() {
decay = l.LiquidDepth()
}

for _, f := range cube.HorizontalFaces() {
p := pos.Side(f)

var realDecay int
side, ok := w.Liquid(p)
if !ok {
sideB := w.Block(p)
if _, ok = sideB.(LiquidRemovable); !ok {
JustTalDevelops marked this conversation as resolved.
Show resolved Hide resolved
continue
}
side, ok = w.Liquid(p.Side(cube.FaceDown))
if !ok {
continue
}
if side.LiquidFalling() {
continue
}
realDecay = side.LiquidDepth() - (decay - 8)
} else if !side.LiquidFalling() {
realDecay = side.LiquidDepth() - decay
}
v = v.Sub(p.Sub(pos).Vec3().Mul(float64(realDecay)))
}
if l.LiquidFalling() {
for _, f := range cube.HorizontalFaces() {
p := pos.Side(f)
if !canFlowInto(l, w, p, true) || !canFlowInto(l, w, p.Side(cube.FaceUp), true) {
v = v.Normalize().Add(mgl64.Vec3{0, -6})
}
}
}
return v.Normalize()
}

// tickLiquid ticks the liquid block passed at a specific position in the world. Depending on the surroundings
// and the liquid block, the liquid will either spread or decrease in depth. Additionally, the liquid might
// be turned into a solid block if a different liquid is next to it.
Expand Down
2 changes: 2 additions & 0 deletions server/entity/direction.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ func EyePosition(e world.Entity) mgl64.Vec3 {
pos := e.Position()
if eyed, ok := e.(Eyed); ok {
pos[1] += eyed.EyeHeight()
} else {
pos[1] += e.BBox().Height() * 0.85
}
return pos
}
39 changes: 38 additions & 1 deletion server/entity/item.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ func NewItem(i item.Stack, pos mgl64.Vec3) *Item {

it := &Item{i: i, pickupDelay: 10, c: &MovementComputer{
Gravity: 0.04,
DragBeforeGravity: true,
Drag: 0.02,
DragBeforeGravity: true,
}}
it.transform = newTransform(it, pos)
return it
Expand Down Expand Up @@ -88,13 +88,50 @@ func (it *Item) Tick(w *world.World, current int64) {
return
}

it.tickLiquid(w, m.vel)
if it.pickupDelay == 0 {
it.checkNearby(w, m.pos)
} else if it.pickupDelay != math.MaxInt16 {
it.pickupDelay--
}
}

// tickLiquid ticks the item's liquid velocity and updates it as necessary.
func (it *Item) tickLiquid(w *world.World, vel mgl64.Vec3) {
if l, ok := it.insideLiquid(EyePosition(it), w); ok {
vel[1] += it.c.Gravity
vel[1] /= 1 - it.c.Drag
if vel[1] < 0.06 {
vel[1] += 0.0005
}
switch l.(type) {
case block.Water:
vel[0] *= 0.99
vel[2] *= 0.99
case block.Lava:
vel[0] *= 0.95
vel[2] *= 0.95
}
it.SetVelocity(vel)
}
}

// liquidLevel is the lowest distance the item can be in liquid.
const liquidLevel = 0.11111111

// insideLiquid returns true if the item is currently in a liquid.
func (it *Item) insideLiquid(pos mgl64.Vec3, w *world.World) (world.Liquid, bool) {
blockPos := cube.PosFromVec3(pos)
if l, ok := w.Liquid(blockPos); ok {
d := float64(l.SpreadDecay()) + 1
if l.LiquidFalling() {
d = 1
}
return l, pos.Y() < (blockPos.Side(cube.FaceUp).Vec3().Y())-(d/9-liquidLevel)
}
return nil, false
}

// checkNearby checks the entities of the chunks around for item collectors and other item stacks. If a
// collector is found in range, the item will be picked up. If another item stack with the same item type is
// found in range, the item stacks will merge.
Expand Down
36 changes: 34 additions & 2 deletions server/entity/movement.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package entity

import (
"github.com/df-mc/dragonfly/server/block"
"github.com/df-mc/dragonfly/server/block/cube"
"github.com/df-mc/dragonfly/server/world"
"github.com/go-gl/mathgl/mgl64"
Expand Down Expand Up @@ -67,7 +68,7 @@ func (c *MovementComputer) TickMovement(e world.Entity, pos, vel mgl64.Vec3, yaw
viewers := w.Viewers(pos)

velBefore := vel
vel = c.applyHorizontalForces(w, pos, c.applyVerticalForces(vel))
vel = c.applyLiquidFlow(e, pos, c.applyHorizontalForces(w, pos, c.applyVerticalForces(vel)))
dPos, vel := c.checkCollision(e, pos, vel)

return &Movement{v: viewers, e: e,
Expand Down Expand Up @@ -101,9 +102,10 @@ func (c *MovementComputer) applyVerticalForces(vel mgl64.Vec3) mgl64.Vec3 {

// applyHorizontalForces applies friction to the velocity based on the Drag value, reducing it on the X and Z axes.
func (c *MovementComputer) applyHorizontalForces(w *world.World, pos, vel mgl64.Vec3) mgl64.Vec3 {
blockPos := cube.PosFromVec3(pos)
friction := 1 - c.Drag
if c.onGround {
if f, ok := w.Block(cube.PosFromVec3(pos).Side(cube.FaceDown)).(interface {
if f, ok := w.Block(blockPos.Side(cube.FaceDown)).(interface {
Friction() float64
}); ok {
friction *= f.Friction()
Expand All @@ -116,6 +118,36 @@ func (c *MovementComputer) applyHorizontalForces(w *world.World, pos, vel mgl64.
return vel
}

// applyLiquidFlow applies liquid flow to the entity's velocity.
func (c *MovementComputer) applyLiquidFlow(e world.Entity, pos, vel mgl64.Vec3) mgl64.Vec3 {
w := e.World()
box := e.BBox().Grow(-0.001).Translate(pos)

min, max := box.Min(), box.Max()
minX, minY, minZ := int(math.Floor(min[0])), int(math.Floor(min[1])), int(math.Floor(min[2]))
maxX, maxY, maxZ := int(math.Ceil(max[0])), int(math.Ceil(max[1])), int(math.Ceil(max[2]))

flow, i := zeroVec3, 0
for y := minY; y <= maxY; y++ {
for x := minX; x <= maxX; x++ {
for z := minZ; z <= maxZ; z++ {
blockPos := cube.Pos{x, y, z}
if l, ok := w.Liquid(blockPos); ok {
flow = flow.Add(block.FlowVector(blockPos, w, l))
i++
}
}
}
}

if flow.Len() > 0.0 {
flow = flow.Mul(1.0 / float64(i))
flow = flow.Normalize().Mul(0.014)
DaPigGuy marked this conversation as resolved.
Show resolved Hide resolved
return vel.Add(flow)
DaPigGuy marked this conversation as resolved.
Show resolved Hide resolved
}
return vel
}

// checkCollision handles the collision of the entity with blocks, adapting the velocity of the entity if it
// happens to collide with a block.
// The final velocity and the Vec3 that the entity should move is returned.
Expand Down