Skip to content

Commit

Permalink
Merge branch 'refs/heads/master' into feature/entity-improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
Sandertv committed Jan 7, 2025
2 parents b7e9a58 + 16b0d3e commit 9e05eee
Show file tree
Hide file tree
Showing 13 changed files with 137 additions and 52 deletions.
8 changes: 8 additions & 0 deletions server/block/action.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,14 @@ type ContinueCrackAction struct {
// StopCrackAction is a world.BlockAction to make the cracks forming in a block stop and disappear.
type StopCrackAction struct{ action }

// DecoratedPotWobbleAction is a world.BlockAction to make a decorated pot wobble when interacted with.
type DecoratedPotWobbleAction struct {
action
DecoratedPot DecoratedPot
// Success is whether an item was successfully inserted into the decorated pot.
Success bool
}

// action implements the Action interface. Structures in this package may embed it to gets its functionality
// out of the box.
type action struct{}
Expand Down
6 changes: 6 additions & 0 deletions server/block/block.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,12 @@ type EntityInsider interface {
EntityInside(pos cube.Pos, tx *world.Tx, e world.Entity)
}

// ProjectileHitter represents a block that handles being hit by a projectile.
type ProjectileHitter interface {
// ProjectileHit is called when a projectile hits the block.
ProjectileHit(pos cube.Pos, tx *world.Tx, e world.Entity, face cube.Face)
}

// Frictional represents a block that may have a custom friction value. Friction is used for entity drag when the
// entity is on ground. If a block does not implement this interface, it should be assumed that its friction is 0.6.
type Frictional interface {
Expand Down
6 changes: 6 additions & 0 deletions server/block/break_info.go
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,12 @@ func breakBlock(b world.Block, pos cube.Pos, tx *world.Tx) {
}

func breakBlockNoDrops(b world.Block, pos cube.Pos, tx *world.Tx) {
if breakable, ok := b.(Breakable); ok {
breakHandler := breakable.BreakInfo().BreakHandler
if breakHandler != nil {
breakHandler(pos, tx, nil)
}
}
tx.SetBlock(pos, nil, nil)
tx.AddParticle(pos.Vec3Centre(), particle.BlockBreak{Block: b})
}
2 changes: 1 addition & 1 deletion server/block/brewing_stand.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ func (b BrewingStand) DecodeNBT(data map[string]any) any {

// BreakInfo ...
func (b BrewingStand) BreakInfo() BreakInfo {
return newBreakInfo(0.5, alwaysHarvestable, pickaxeEffective, oneOf(b)).withBreakHandler(func(pos cube.Pos, tx *world.Tx, u item.User) {
return newBreakInfo(0.5, alwaysHarvestable, pickaxeEffective, oneOf(BrewingStand{})).withBreakHandler(func(pos cube.Pos, tx *world.Tx, u item.User) {
for _, i := range b.Inventory(tx, pos).Clear() {
dropItem(tx, i, pos.Vec3Centre())
}
Expand Down
44 changes: 30 additions & 14 deletions server/block/campfire.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,23 +51,22 @@ func (Campfire) SideClosed(cube.Pos, cube.Pos, *world.Tx) bool {
// BreakInfo ...
func (c Campfire) BreakInfo() BreakInfo {
return newBreakInfo(2, alwaysHarvestable, axeEffective, func(t item.Tool, enchantments []item.Enchantment) []item.Stack {
var drops []item.Stack
if hasSilkTouch(enchantments) {
drops = append(drops, item.NewStack(c, 1))
} else {
switch c.Type {
case NormalFire():
drops = append(drops, item.NewStack(item.Charcoal{}, 2))
case SoulFire():
drops = append(drops, item.NewStack(SoulSoil{}, 1))
}
return []item.Stack{item.NewStack(Campfire{Type: c.Type}, 1)}
}
switch c.Type {
case NormalFire():
return []item.Stack{item.NewStack(item.Charcoal{}, 2)}
case SoulFire():
return []item.Stack{item.NewStack(SoulSoil{}, 1)}
}
panic("should never happen")
}).withBreakHandler(func(pos cube.Pos, tx *world.Tx, u item.User) {
for _, v := range c.Items {
if !v.Item.Empty() {
drops = append(drops, v.Item)
dropItem(tx, v.Item, pos.Vec3Centre())
}
}
return drops
})
}

Expand Down Expand Up @@ -133,6 +132,10 @@ func (c Campfire) Activate(pos cube.Pos, _ cube.Face, tx *world.Tx, u item.User,
return false
}

if _, ok = tx.Liquid(pos); ok {
return false
}

for i, it := range c.Items {
if it.Item.Empty() {
c.Items[i] = CampfireItem{
Expand Down Expand Up @@ -198,9 +201,22 @@ func (c Campfire) Tick(_ int64, pos cube.Pos, tx *world.Tx) {

// NeighbourUpdateTick ...
func (c Campfire) NeighbourUpdateTick(pos, _ cube.Pos, tx *world.Tx) {
_, ok := tx.Liquid(pos)
liquid, okTwo := tx.Liquid(pos.Side(cube.FaceUp))
if (ok || (okTwo && liquid.LiquidType() == "water")) && !c.Extinguished {
if _, ok := tx.Liquid(pos); ok {
var updated bool
for i, it := range c.Items {
if !it.Item.Empty() {
dropItem(tx, it.Item, pos.Vec3Centre())
c.Items[i].Item, updated = item.Stack{}, true
}
}
if !c.Extinguished {
c.extinguish(pos, tx)
} else if updated {
tx.SetBlock(pos, c, nil)
}
return
}
if liquid, ok := tx.Liquid(pos.Side(cube.FaceUp)); ok && liquid.LiquidType() == "water" && !c.Extinguished {
c.extinguish(pos, tx)
}
}
Expand Down
51 changes: 36 additions & 15 deletions server/block/decorated_pot.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import (
"github.com/df-mc/dragonfly/server/internal/nbtconv"
"github.com/df-mc/dragonfly/server/item"
"github.com/df-mc/dragonfly/server/world"
"github.com/df-mc/dragonfly/server/world/particle"
"github.com/df-mc/dragonfly/server/world/sound"
"github.com/go-gl/mathgl/mgl64"
)

Expand All @@ -31,6 +33,23 @@ type DecoratedPot struct {
Decorations [4]PotDecoration
}

// ProjectileHit ...
func (p DecoratedPot) ProjectileHit(pos cube.Pos, tx *world.Tx, _ world.Entity, _ cube.Face) {
for _, d := range p.Decorations {
if d == nil {
dropItem(tx, item.NewStack(item.Brick{}, 1), pos.Vec3Centre())
continue
}
dropItem(tx, item.NewStack(d, 1), pos.Vec3Centre())
}
breakBlockNoDrops(p, pos, tx)
}

// Pick ...
func (p DecoratedPot) Pick() item.Stack {
return item.NewStack(DecoratedPot{Decorations: p.Decorations}, 1)
}

// ExtractItem ...
func (p DecoratedPot) ExtractItem(h Hopper, pos cube.Pos, tx *world.Tx) bool {
if p.Item.Empty() {
Expand Down Expand Up @@ -61,10 +80,25 @@ func (p DecoratedPot) InsertItem(h Hopper, pos cube.Pos, tx *world.Tx) bool {
return false
}

// wobble ...
func (p DecoratedPot) wobble(pos cube.Pos, tx *world.Tx, success bool) {
for _, v := range tx.Viewers(pos.Vec3Centre()) {
v.ViewBlockAction(pos, DecoratedPotWobbleAction{DecoratedPot: p, Success: success})
}

if success {
tx.AddParticle(pos.Vec3Middle().Add(mgl64.Vec3{0, 1.2}), particle.DustPlume{})
tx.PlaySound(pos.Vec3Centre(), sound.DecoratedPotInserted{Progress: float64(p.Item.Count()) / float64(p.Item.MaxCount())})
} else {
tx.PlaySound(pos.Vec3Centre(), sound.DecoratedPotInsertFailed{})
}
}

// Activate ...
func (p DecoratedPot) Activate(pos cube.Pos, _ cube.Face, tx *world.Tx, u item.User, ctx *item.UseContext) bool {
held, _ := u.HeldItems()
if held.Empty() || !p.Item.Comparable(held) || p.Item.Count() == p.Item.MaxCount() {
p.wobble(pos, tx, false)
return false
}

Expand All @@ -74,27 +108,14 @@ func (p DecoratedPot) Activate(pos cube.Pos, _ cube.Face, tx *world.Tx, u item.U
p.Item = p.Item.Grow(1)
}
tx.SetBlock(pos, p, nil)
p.wobble(pos, tx, true)
ctx.SubtractFromCount(1)
return true
}

// BreakInfo ...
func (p DecoratedPot) BreakInfo() BreakInfo {
return newBreakInfo(0, alwaysHarvestable, nothingEffective, func(tool item.Tool, enchantments []item.Enchantment) []item.Stack {
if hasSilkTouch(enchantments) {
p.Item = item.Stack{}
return []item.Stack{item.NewStack(p, 1)}
}
var drops []item.Stack
for _, d := range p.Decorations {
if d == nil {
drops = append(drops, item.NewStack(item.Brick{}, 1))
continue
}
drops = append(drops, item.NewStack(d, 1))
}
return drops
}).withBreakHandler(func(pos cube.Pos, tx *world.Tx, u item.User) {
return newBreakInfo(0, alwaysHarvestable, nothingEffective, oneOf(DecoratedPot{Decorations: p.Decorations})).withBreakHandler(func(pos cube.Pos, tx *world.Tx, u item.User) {
if !p.Item.Empty() {
dropItem(tx, p.Item, pos.Vec3Centre())
}
Expand Down
19 changes: 4 additions & 15 deletions server/block/explosion.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,27 +137,16 @@ func (c ExplosionConfig) Explode(tx *world.Tx, explosionPos mgl64.Vec3) {
if explodable, ok := bl.(Explodable); ok {
explodable.Explode(explosionPos, pos, tx, c)
} else if breakable, ok := bl.(Breakable); ok {
breakHandler := breakable.BreakInfo().BreakHandler
if breakHandler != nil {
breakHandler(pos, tx, nil)
}
tx.SetBlock(pos, nil, nil)
if c.ItemDropChance > r.Float64() {
for _, drop := range breakable.BreakInfo().Drops(item.ToolNone{}, nil) {
dropItem(tx, drop, pos.Vec3Centre())
}
}

if container, ok := bl.(Container); ok {
if cb, ok := bl.(Chest); ok {
if cb.Paired() {
pairPos := cb.pairPos(pos)
if _, pair, ok := cb.unpair(tx, pos); ok {
cb.paired = false
tx.SetBlock(pairPos, pair, nil)
}
}
}
for _, i := range container.Inventory(tx, pos).Clear() {
dropItem(tx, i, pos.Vec3())
}
}
}
}
if c.SpawnFire {
Expand Down
6 changes: 1 addition & 5 deletions server/block/jukebox.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,7 @@ func (j Jukebox) FuelInfo() item.FuelInfo {

// BreakInfo ...
func (j Jukebox) BreakInfo() BreakInfo {
d := []item.Stack{item.NewStack(Jukebox{}, 1)}
if !j.Item.Empty() {
d = append(d, j.Item)
}
return newBreakInfo(0.8, alwaysHarvestable, axeEffective, simpleDrops(d...)).withBreakHandler(func(pos cube.Pos, tx *world.Tx, u item.User) {
return newBreakInfo(2, alwaysHarvestable, axeEffective, oneOf(Jukebox{})).withBlastResistance(30).withBreakHandler(func(pos cube.Pos, tx *world.Tx, u item.User) {
if _, hasDisc := j.Disc(); hasDisc {
dropItem(tx, j.Item, pos.Vec3())
tx.PlaySound(pos.Vec3Centre(), sound.MusicDiscEnd{})
Expand Down
7 changes: 7 additions & 0 deletions server/block/tnt.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@ type TNT struct {
solid
}

// ProjectileHit ...
func (t TNT) ProjectileHit(pos cube.Pos, tx *world.Tx, e world.Entity, _ cube.Face) {
if f, ok := e.(flammableEntity); ok && f.OnFireDuration() > 0 {
t.Ignite(pos, tx, nil)
}
}

// Activate ...
func (t TNT) Activate(pos cube.Pos, _ cube.Face, tx *world.Tx, u item.User, ctx *item.UseContext) bool {
held, _ := u.HeldItems()
Expand Down
4 changes: 2 additions & 2 deletions server/entity/projectile.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,8 +172,8 @@ func (lt *ProjectileBehaviour) Tick(e *Ent, tx *world.Tx) *Movement {
}
case trace.BlockResult:
bpos := r.BlockPosition()
if t, ok := tx.Block(bpos).(block.TNT); ok && e.OnFireDuration() > 0 {
t.Ignite(bpos, tx, nil)
if h, ok := tx.Block(bpos).(block.ProjectileHitter); ok {
h.ProjectileHit(bpos, tx, e, r.Face())
}
if lt.conf.SurviveBlockCollision {
lt.hitBlockSurviving(e, r, m, tx)
Expand Down
23 changes: 23 additions & 0 deletions server/session/world.go
Original file line number Diff line number Diff line change
Expand Up @@ -453,6 +453,11 @@ func (s *Session) ViewParticle(pos mgl64.Vec3, p world.Particle) {
EventType: packet.LevelEventParticleLegacyEvent | 10,
Position: vec64To32(pos),
})
case particle.DustPlume:
s.writePacket(&packet.LevelEvent{
EventType: packet.LevelEventParticleLegacyEvent | 88,
Position: vec64To32(pos),
})
}
}

Expand Down Expand Up @@ -810,6 +815,16 @@ func (s *Session) playSound(pos mgl64.Vec3, t world.Sound, disableRelative bool)
EventType: packet.LevelEventSoundTotemUsed,
Position: vec64To32(pos),
})
case sound.DecoratedPotInserted:
s.writePacket(&packet.PlaySound{
SoundName: "block.decorated_pot.insert",
Position: vec64To32(pos),
Volume: 1,
Pitch: 0.7 + 0.5*float32(so.Progress),
})
return
case sound.DecoratedPotInsertFailed:
pk.SoundType = packet.SoundEventDecoratedPotInsertFail
}
s.writePacket(pk)
}
Expand Down Expand Up @@ -1152,6 +1167,14 @@ func (s *Session) ViewBlockAction(pos cube.Pos, a world.BlockAction) {
Position: vec64To32(pos.Vec3()),
EventData: int32(65535 / (t.BreakTime.Seconds() * 20)),
})
case block.DecoratedPotWobbleAction:
nbt := t.DecoratedPot.EncodeNBT()
nbt["x"], nbt["y"], nbt["z"] = blockPos.X(), blockPos.Y(), blockPos.Z()
nbt["animation"] = boolByte(t.Success) + 1
s.writePacket(&packet.BlockActorData{
Position: blockPos,
NBTData: nbt,
})
}
}

Expand Down
3 changes: 3 additions & 0 deletions server/world/particle/block.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,9 @@ type LavaDrip struct{ particle }
// Lava is a particle that shows up randomly above lava.
type Lava struct{ particle }

// DustPlume is a particle that shows up when an item is successfully inserted into a decorated pot.
type DustPlume struct{ particle }

// particle serves as a base for all particles in this package.
type particle struct{}

Expand Down
10 changes: 10 additions & 0 deletions server/world/sound/block.go
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,16 @@ type WaxRemoved struct{ sound }
// CopperScraped is a sound played when a player scrapes a copper block to reduce its oxidation level.
type CopperScraped struct{ sound }

// DecoratedPotInserted is a sound played when an item is successfully inserted into a decorated pot.
type DecoratedPotInserted struct {
sound
// Progress is how much of the decorated pot has been filled.
Progress float64
}

// DecoratedPotInsertFailed is a sound played when an item fails to be inserted into a decorated pot.
type DecoratedPotInsertFailed struct{ sound }

// sound implements the world.Sound interface.
type sound struct{}

Expand Down

0 comments on commit 9e05eee

Please sign in to comment.