From 3346de42382e20dc4e1707d647d40c41b091e4e3 Mon Sep 17 00:00:00 2001 From: T14Raptor Date: Tue, 12 Jul 2022 11:05:35 -0400 Subject: [PATCH 01/41] explosion/explosion.go: Third attempt at implementing explosions. --- main.go | 38 ++++++- server/block/bedrock.go | 4 + server/block/dirt.go | 4 + server/block/grass.go | 4 + server/entity/damage/source.go | 5 + server/world/explosion/explosion.go | 168 ++++++++++++++++++++++++++++ 6 files changed, 222 insertions(+), 1 deletion(-) create mode 100644 server/world/explosion/explosion.go diff --git a/main.go b/main.go index ce94041e2..83f5016d1 100644 --- a/main.go +++ b/main.go @@ -3,11 +3,17 @@ package main import ( "fmt" "github.com/df-mc/dragonfly/server" + "github.com/df-mc/dragonfly/server/event" + "github.com/df-mc/dragonfly/server/player" "github.com/df-mc/dragonfly/server/player/chat" + "github.com/df-mc/dragonfly/server/world" + "github.com/df-mc/dragonfly/server/world/explosion" "github.com/pelletier/go-toml" "github.com/sirupsen/logrus" "io/ioutil" "os" + "runtime/pprof" + "time" ) func main() { @@ -28,10 +34,40 @@ func main() { log.Fatalln(err) } - for srv.Accept(nil) { + go func() { + time.Sleep(5 * time.Second) + + f, err := os.Create("mem.pprof") + if err != nil { + panic(err) + } + pprof.WriteHeapProfile(f) + f.Close() + + fmt.Println("done") + }() + + for srv.Accept(func(p *player.Player) { + p.SetGameMode(world.GameModeSurvival) + p.Handle(h{p: p}) + }) { } } +type h struct { + player.NopHandler + + p *player.Player +} + +func (h h) HandleChat(*event.Context, *string) { + explosion.Config{ + World: h.p.World(), + Pos: h.p.Position(), + Size: 4, + }.Do() +} + // readConfig reads the configuration from the config.toml file, or creates the file if it does not yet exist. func readConfig() (server.Config, error) { c := server.DefaultConfig() diff --git a/server/block/bedrock.go b/server/block/bedrock.go index bb10292c9..d671f0abb 100644 --- a/server/block/bedrock.go +++ b/server/block/bedrock.go @@ -11,6 +11,10 @@ type Bedrock struct { InfiniteBurning bool } +func (Bedrock) BlastResistance() float64 { + return 3600000 +} + // EncodeItem ... func (Bedrock) EncodeItem() (name string, meta int16) { return "minecraft:bedrock", 0 diff --git a/server/block/dirt.go b/server/block/dirt.go index 4e3a1cf29..bb0ab6e94 100644 --- a/server/block/dirt.go +++ b/server/block/dirt.go @@ -14,6 +14,10 @@ type Dirt struct { Coarse bool } +func (Dirt) BlastResistance() float64 { + return 0.5 +} + // SoilFor ... func (d Dirt) SoilFor(block world.Block) bool { switch block.(type) { diff --git a/server/block/grass.go b/server/block/grass.go index 54bc5a839..60925e672 100644 --- a/server/block/grass.go +++ b/server/block/grass.go @@ -11,6 +11,10 @@ type Grass struct { solid } +func (Grass) BlastResistance() float64 { + return 0.6 +} + // plantSelection are the plants that are picked from when a bone meal is attempted. // TODO: Base plant selection on current biome. var plantSelection = []world.Block{ diff --git a/server/entity/damage/source.go b/server/entity/damage/source.go index 765b353df..39621b02d 100644 --- a/server/entity/damage/source.go +++ b/server/entity/damage/source.go @@ -73,6 +73,9 @@ type ( // Block is the block that caused the damage. Block world.Block } + + // SourceExplosion ... + SourceExplosion struct{} ) func (SourceFall) ReducedByArmour() bool { return false } @@ -105,3 +108,5 @@ func (SourceProjectile) ReducedByResistance() bool { return true } func (SourceProjectile) ReducedByArmour() bool { return true } func (SourceBlock) ReducedByResistance() bool { return true } func (SourceBlock) ReducedByArmour() bool { return true } +func (SourceExplosion) ReducedByResistance() bool { return true } +func (SourceExplosion) ReducedByArmour() bool { return true } diff --git a/server/world/explosion/explosion.go b/server/world/explosion/explosion.go new file mode 100644 index 000000000..57f63c716 --- /dev/null +++ b/server/world/explosion/explosion.go @@ -0,0 +1,168 @@ +package explosion + +import ( + "github.com/df-mc/dragonfly/server/block" + "github.com/df-mc/dragonfly/server/block/cube" + "github.com/df-mc/dragonfly/server/block/cube/trace" + "github.com/df-mc/dragonfly/server/entity" + "github.com/df-mc/dragonfly/server/entity/damage" + "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" + "math" + "math/rand" + "time" +) + +var rays = make([]mgl64.Vec3, 0, 1352) + +func init() { + for x := 0.0; x < 16; x++ { + for y := 0.0; y < 16; y++ { + for z := 0.0; z < 16; z++ { + if x != 0 && x != 15 && y != 0 && y != 15 && z != 0 && z != 15 { + continue + } + rays = append(rays, mgl64.Vec3{x/15*2 - 1, y/15*2 - 1, z/15*2 - 1}.Normalize().Mul(0.3)) + } + } + } +} + +type ( + BlockFunc func(c Config, pos cube.Pos) + EntityFunc func(c Config, e world.Entity) +) + +var ( + DefaultBlockFunc BlockFunc = func(c Config, pos cube.Pos) { + // TODO + c.World.SetBlock(pos, nil, nil) + } + DefaultEntityFunc EntityFunc = func(c Config, e world.Entity) { + // TODO: Account for item entities etc. + living, ok := e.(entity.Living) + if !ok { + return + } + pos := e.Position() + diff := pos.Sub(c.Pos) + + impact := (1 - diff.Len()) * exposure(c.Pos, e) + + dmg := math.Floor(((impact*impact+impact)/2)*8*c.Size*2 + 1) + + living.Hurt(dmg, damage.SourceExplosion{}) + living.KnockBack(c.Pos, impact, diff.Normalize().Mul(impact)[0]) + } +) + +type Config struct { + World *world.World + Pos mgl64.Vec3 + Size float64 + Rand rand.Source + // TODO: Fire spawning + + BlockFunc BlockFunc + EntityFunc EntityFunc +} + +func (c Config) Do() { + if c.Rand == nil { + c.Rand = rand.NewSource(time.Now().UnixNano()) + } + r := rand.New(c.Rand) + if c.BlockFunc == nil { + c.BlockFunc = DefaultBlockFunc + } + if c.EntityFunc == nil { + c.EntityFunc = DefaultEntityFunc + } + d := c.Size * 2 + bb := cube.Box( + math.Floor(c.Pos[0]-d-1), + math.Ceil(c.Pos[0]+d+1), + math.Floor(c.Pos[1]-d-1), + math.Ceil(c.Pos[1]+d+1), + math.Floor(c.Pos[2]-d-1), + math.Ceil(c.Pos[2]+d+1), + ) + + for _, e := range c.World.EntitiesWithin(bb.Grow(2), nil) { + entityPos := e.Position() + if !e.BBox().Translate(entityPos).IntersectsWith(bb) { + continue + } + if entityPos.Sub(c.Pos).Len() <= d { + c.EntityFunc(c, e) + } + } + + var affectedBlocks []cube.Pos + for _, ray := range rays { + pos := c.Pos + for blastForce := c.Size * (0.7 + r.Float64()*0.6); blastForce > 0.0; blastForce -= 0.225 { + current := cube.PosFromVec3(pos) + bl := c.World.Block(current) + if r, ok := bl.(interface{ BlastResistance() float64 }); ok { + if blastForce -= (r.BlastResistance() + 0.3) * 0.3; blastForce > 0 { + affectedBlocks = append(affectedBlocks, current) + } + } + pos = pos.Add(ray) + } + } + for _, pos := range affectedBlocks { + c.BlockFunc(c, pos) + } + + c.World.AddParticle(c.Pos, particle.HugeExplosion{}) + c.World.PlaySound(c.Pos, sound.Explosion{}) +} + +// TODO +func exposure(origin mgl64.Vec3, e world.Entity) float64 { + w := e.World() + pos := e.Position() + bb := e.BBox().Translate(pos) + min, max := bb.Min(), bb.Max() + diff := max.Sub(min).Mul(2.0).Add(mgl64.Vec3{1, 1, 1}) + double4 := 1.0 / diff[0] + double5 := 1.0 / diff[1] + double6 := 1.0 / diff[2] + double7 := (1.0 - math.Floor(1.0/double4)*double4) / 2.0 + double8 := (1.0 - math.Floor(1.0/double6)*double6) / 2.0 + if double4 < 0.0 || double5 < 0.0 || double6 < 0.0 { + return 0.0 + } + integer14 := 0.0 + integer15 := 0.0 + for float16 := 0.0; float16 <= 1.0; float16 += double4 { + for float17 := 0.0; float17 <= 1.0; float17 += double5 { + for float18 := 0.0; float18 <= 1.0; float18 += double6 { + dck2 := mgl64.Vec3{ + lerp(float16, min[0], max[0]) + double7, + lerp(float17, min[1], max[1]), + lerp(float18, min[2], max[2]) + double8, + } + var collides bool + trace.TraverseBlocks(dck2, origin, func(pos cube.Pos) (con bool) { + _, ok := w.Block(pos).(block.Air) + collides = !ok + return ok + }) + if collides { + integer14++ + } + integer15++ + } + } + } + return integer14 / integer15 +} + +func lerp(v, v1, t float64) float64 { + return (1-t)*v + t*v1 +} From b172c4d6fa35acfdfe281373d04a94bce9314035 Mon Sep 17 00:00:00 2001 From: T14Raptor Date: Thu, 14 Jul 2022 04:21:51 -0400 Subject: [PATCH 02/41] "Explodable" api --- server/block/bedrock.go | 9 +++ server/block/block.go | 18 +++++ server/block/dirt.go | 16 +++- server/block/grass.go | 15 +++- server/entity/arrow.go | 6 ++ server/entity/ender_pearl.go | 6 ++ server/entity/experience_orb.go | 6 ++ server/entity/falling_block.go | 6 ++ server/entity/item.go | 6 ++ server/entity/snowball.go | 6 ++ server/entity/splash_potion.go | 6 ++ server/world/explosion/explosion.go | 113 +++++++++++++++------------- 12 files changed, 152 insertions(+), 61 deletions(-) diff --git a/server/block/bedrock.go b/server/block/bedrock.go index d671f0abb..35ef77882 100644 --- a/server/block/bedrock.go +++ b/server/block/bedrock.go @@ -1,5 +1,10 @@ package block +import ( + "github.com/df-mc/dragonfly/server/block/cube" + "github.com/df-mc/dragonfly/server/world/explosion" +) + // Bedrock is a block that is indestructible in survival. type Bedrock struct { solid @@ -11,10 +16,14 @@ type Bedrock struct { InfiniteBurning bool } +// BlastResistance ... func (Bedrock) BlastResistance() float64 { return 3600000 } +// Explode ... +func (b Bedrock) Explode(cube.Pos, explosion.Config) {} + // EncodeItem ... func (Bedrock) EncodeItem() (name string, meta int16) { return "minecraft:bedrock", 0 diff --git a/server/block/block.go b/server/block/block.go index 91a068c44..549d965a9 100644 --- a/server/block/block.go +++ b/server/block/block.go @@ -5,7 +5,10 @@ import ( "github.com/df-mc/dragonfly/server/entity" "github.com/df-mc/dragonfly/server/item" "github.com/df-mc/dragonfly/server/world" + "github.com/df-mc/dragonfly/server/world/explosion" "github.com/df-mc/dragonfly/server/world/sound" + "github.com/go-gl/mathgl/mgl64" + "math/rand" ) // Activatable represents a block that may be activated by a viewer of the world. When activated, the block @@ -205,6 +208,21 @@ func (g gravityAffected) fall(b world.Block, pos cube.Pos, w *world.World) { } } +type explodable struct { + b BreakInfo +} + +func (e explodable) Explode(pos cube.Pos, c explosion.Config) { + c.World.SetBlock(pos, nil, nil) + if 1/c.Size > rand.Float64() { + for _, drop := range e.b.Drops(item.ToolNone{}, nil) { + it := entity.NewItem(drop, pos.Vec3Centre()) + it.SetVelocity(mgl64.Vec3{rand.Float64()*0.2 - 1, 0.2, rand.Float64()*0.2 - 1}) + c.World.AddEntity(entity.NewItem(drop, pos.Vec3Centre())) + } + } +} + // Flammable is an interface for blocks that can catch on fire. type Flammable interface { // FlammabilityInfo returns information about a block's behavior involving fire. diff --git a/server/block/dirt.go b/server/block/dirt.go index bb0ab6e94..377d0fd6b 100644 --- a/server/block/dirt.go +++ b/server/block/dirt.go @@ -1,7 +1,9 @@ package block import ( + "github.com/df-mc/dragonfly/server/block/cube" "github.com/df-mc/dragonfly/server/world" + "github.com/df-mc/dragonfly/server/world/explosion" ) // Dirt is a block found abundantly in most biomes under a layer of grass blocks at the top of the normal @@ -14,10 +16,6 @@ type Dirt struct { Coarse bool } -func (Dirt) BlastResistance() float64 { - return 0.5 -} - // SoilFor ... func (d Dirt) SoilFor(block world.Block) bool { switch block.(type) { @@ -45,6 +43,16 @@ func (d Dirt) Shovel() (world.Block, bool) { return DirtPath{}, true } +// BlastResistance ... +func (Dirt) BlastResistance() float64 { + return 0.5 +} + +// Explode ... +func (d Dirt) Explode(pos cube.Pos, c explosion.Config) { + explodable{b: d.BreakInfo()}.Explode(pos, c) +} + // EncodeItem ... func (d Dirt) EncodeItem() (name string, meta int16) { if d.Coarse { diff --git a/server/block/grass.go b/server/block/grass.go index 60925e672..ab2682586 100644 --- a/server/block/grass.go +++ b/server/block/grass.go @@ -3,6 +3,7 @@ package block import ( "github.com/df-mc/dragonfly/server/block/cube" "github.com/df-mc/dragonfly/server/world" + "github.com/df-mc/dragonfly/server/world/explosion" "math/rand" ) @@ -11,10 +12,6 @@ type Grass struct { solid } -func (Grass) BlastResistance() float64 { - return 0.6 -} - // plantSelection are the plants that are picked from when a bone meal is attempted. // TODO: Base plant selection on current biome. var plantSelection = []world.Block{ @@ -47,6 +44,16 @@ func (g Grass) SoilFor(block world.Block) bool { return false } +// BlastResistance ... +func (g Grass) BlastResistance() float64 { + return 0.6 +} + +// Explode ... +func (g Grass) Explode(pos cube.Pos, c explosion.Config) { + explodable{b: g.BreakInfo()}.Explode(pos, c) +} + // RandomTick handles the ticking of grass, which may or may not result in the spreading of grass onto dirt. func (g Grass) RandomTick(pos cube.Pos, w *world.World, r *rand.Rand) { aboveLight := w.Light(pos.Add(cube.Pos{0, 1})) diff --git a/server/entity/arrow.go b/server/entity/arrow.go index 98112cae1..b165e401e 100644 --- a/server/entity/arrow.go +++ b/server/entity/arrow.go @@ -8,6 +8,7 @@ import ( "github.com/df-mc/dragonfly/server/item" "github.com/df-mc/dragonfly/server/item/potion" "github.com/df-mc/dragonfly/server/world" + "github.com/df-mc/dragonfly/server/world/explosion" "github.com/df-mc/dragonfly/server/world/sound" "github.com/go-gl/mathgl/mgl64" "math" @@ -232,6 +233,11 @@ func (a *Arrow) New(pos, vel mgl64.Vec3, yaw, pitch float64, owner world.Entity, return arrow } +// Explode ... +func (a *Arrow) Explode(c explosion.Config, impact float64) { + a.SetVelocity(a.Position().Sub(c.Pos).Normalize().Mul(impact)) +} + // Owner returns the world.Entity that fired the Arrow, or nil if it did not have any. func (a *Arrow) Owner() world.Entity { a.mu.Lock() diff --git a/server/entity/ender_pearl.go b/server/entity/ender_pearl.go index 29fd2bb78..7a9726562 100644 --- a/server/entity/ender_pearl.go +++ b/server/entity/ender_pearl.go @@ -6,6 +6,7 @@ import ( "github.com/df-mc/dragonfly/server/entity/damage" "github.com/df-mc/dragonfly/server/internal/nbtconv" "github.com/df-mc/dragonfly/server/world" + "github.com/df-mc/dragonfly/server/world/explosion" "github.com/df-mc/dragonfly/server/world/particle" "github.com/df-mc/dragonfly/server/world/sound" "github.com/go-gl/mathgl/mgl64" @@ -131,6 +132,11 @@ func (e *EnderPearl) New(pos, vel mgl64.Vec3, yaw, pitch float64) world.Entity { return pearl } +// Explode ... +func (e *EnderPearl) Explode(c explosion.Config, impact float64) { + e.SetVelocity(e.Position().Sub(c.Pos).Normalize().Mul(impact)) +} + // Owner ... func (e *EnderPearl) Owner() world.Entity { e.mu.Lock() diff --git a/server/entity/experience_orb.go b/server/entity/experience_orb.go index 00634b713..abd75fc5c 100644 --- a/server/entity/experience_orb.go +++ b/server/entity/experience_orb.go @@ -4,6 +4,7 @@ import ( "github.com/df-mc/dragonfly/server/block/cube" "github.com/df-mc/dragonfly/server/internal/nbtconv" "github.com/df-mc/dragonfly/server/world" + "github.com/df-mc/dragonfly/server/world/explosion" "github.com/go-gl/mathgl/mgl64" "golang.org/x/exp/slices" "math" @@ -136,6 +137,11 @@ func (e *ExperienceOrb) Tick(w *world.World, current int64) { } } +// Explode ... +func (e *ExperienceOrb) Explode(explosion.Config, float64) { + _ = e.Close() +} + // DecodeNBT decodes the properties in a map to an Item and returns a new Item entity. func (e *ExperienceOrb) DecodeNBT(data map[string]any) any { o := NewExperienceOrb(nbtconv.MapVec3(data, "Pos"), int(nbtconv.Map[int32](data, "Value"))) diff --git a/server/entity/falling_block.go b/server/entity/falling_block.go index b491df6db..338d326eb 100644 --- a/server/entity/falling_block.go +++ b/server/entity/falling_block.go @@ -8,6 +8,7 @@ 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/explosion" "github.com/go-gl/mathgl/mgl64" "math" "math/rand" @@ -129,6 +130,11 @@ func (f *FallingBlock) Tick(w *world.World, _ int64) { } } +// Explode ... +func (f *FallingBlock) Explode(explosion.Config, float64) { + _ = f.Close() +} + // DecodeNBT decodes the relevant data from the entity NBT passed and returns a new FallingBlock entity. func (f *FallingBlock) DecodeNBT(data map[string]any) any { b := nbtconv.MapBlock(data, "FallingBlock") diff --git a/server/entity/item.go b/server/entity/item.go index 33072912d..32935b093 100644 --- a/server/entity/item.go +++ b/server/entity/item.go @@ -6,6 +6,7 @@ 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/explosion" "github.com/go-gl/mathgl/mgl64" "math" "time" @@ -166,6 +167,11 @@ func (it *Item) collect(w *world.World, collector Collector, pos mgl64.Vec3) { _ = it.Close() } +// Explode ... +func (it *Item) Explode(explosion.Config, float64) { + _ = it.Close() +} + // DecodeNBT decodes the properties in a map to an Item and returns a new Item entity. func (it *Item) DecodeNBT(data map[string]any) any { i := nbtconv.MapItem(data, "Item") diff --git a/server/entity/snowball.go b/server/entity/snowball.go index d2eb7aa84..dbeb3fb60 100644 --- a/server/entity/snowball.go +++ b/server/entity/snowball.go @@ -6,6 +6,7 @@ import ( "github.com/df-mc/dragonfly/server/entity/damage" "github.com/df-mc/dragonfly/server/internal/nbtconv" "github.com/df-mc/dragonfly/server/world" + "github.com/df-mc/dragonfly/server/world/explosion" "github.com/df-mc/dragonfly/server/world/particle" "github.com/go-gl/mathgl/mgl64" ) @@ -112,6 +113,11 @@ func (s *Snowball) New(pos, vel mgl64.Vec3, yaw, pitch float64) world.Entity { return snow } +// Explode ... +func (s *Snowball) Explode(c explosion.Config, impact float64) { + s.SetVelocity(s.Position().Sub(c.Pos).Normalize().Mul(impact)) +} + // Owner ... func (s *Snowball) Owner() world.Entity { s.mu.Lock() diff --git a/server/entity/splash_potion.go b/server/entity/splash_potion.go index 20d46c29c..0dd8b12e8 100644 --- a/server/entity/splash_potion.go +++ b/server/entity/splash_potion.go @@ -7,6 +7,7 @@ import ( "github.com/df-mc/dragonfly/server/internal/nbtconv" "github.com/df-mc/dragonfly/server/item/potion" "github.com/df-mc/dragonfly/server/world" + "github.com/df-mc/dragonfly/server/world/explosion" "github.com/df-mc/dragonfly/server/world/particle" "github.com/df-mc/dragonfly/server/world/sound" "github.com/go-gl/mathgl/mgl64" @@ -176,6 +177,11 @@ func (s *SplashPotion) New(pos, vel mgl64.Vec3, yaw, pitch float64, t potion.Pot return splash } +// Explode ... +func (s *SplashPotion) Explode(c explosion.Config, impact float64) { + s.SetVelocity(s.Position().Sub(c.Pos).Normalize().Mul(impact)) +} + // Owner ... func (s *SplashPotion) Owner() world.Entity { s.mu.Lock() diff --git a/server/world/explosion/explosion.go b/server/world/explosion/explosion.go index 57f63c716..1dfdd2ab2 100644 --- a/server/world/explosion/explosion.go +++ b/server/world/explosion/explosion.go @@ -1,14 +1,9 @@ package explosion import ( - "github.com/df-mc/dragonfly/server/block" "github.com/df-mc/dragonfly/server/block/cube" "github.com/df-mc/dragonfly/server/block/cube/trace" - "github.com/df-mc/dragonfly/server/entity" - "github.com/df-mc/dragonfly/server/entity/damage" "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" "math" "math/rand" @@ -30,43 +25,24 @@ func init() { } } -type ( - BlockFunc func(c Config, pos cube.Pos) - EntityFunc func(c Config, e world.Entity) -) - -var ( - DefaultBlockFunc BlockFunc = func(c Config, pos cube.Pos) { - // TODO - c.World.SetBlock(pos, nil, nil) - } - DefaultEntityFunc EntityFunc = func(c Config, e world.Entity) { - // TODO: Account for item entities etc. - living, ok := e.(entity.Living) - if !ok { - return - } - pos := e.Position() - diff := pos.Sub(c.Pos) - - impact := (1 - diff.Len()) * exposure(c.Pos, e) - - dmg := math.Floor(((impact*impact+impact)/2)*8*c.Size*2 + 1) +type ExplodableEntity interface { + Explode(c Config, impact float64) +} - living.Hurt(dmg, damage.SourceExplosion{}) - living.KnockBack(c.Pos, impact, diff.Normalize().Mul(impact)[0]) - } -) +type ExplodableBlock interface { + BlastResistance() float64 + Explode(pos cube.Pos, c Config) +} type Config struct { World *world.World Pos mgl64.Vec3 Size float64 Rand rand.Source - // TODO: Fire spawning + Fire bool - BlockFunc BlockFunc - EntityFunc EntityFunc + Sound world.Sound + Particle world.Particle } func (c Config) Do() { @@ -74,12 +50,6 @@ func (c Config) Do() { c.Rand = rand.NewSource(time.Now().UnixNano()) } r := rand.New(c.Rand) - if c.BlockFunc == nil { - c.BlockFunc = DefaultBlockFunc - } - if c.EntityFunc == nil { - c.EntityFunc = DefaultEntityFunc - } d := c.Size * 2 bb := cube.Box( math.Floor(c.Pos[0]-d-1), @@ -91,22 +61,25 @@ func (c Config) Do() { ) for _, e := range c.World.EntitiesWithin(bb.Grow(2), nil) { - entityPos := e.Position() - if !e.BBox().Translate(entityPos).IntersectsWith(bb) { + pos := e.Position() + if !e.BBox().Translate(pos).IntersectsWith(bb) { continue } - if entityPos.Sub(c.Pos).Len() <= d { - c.EntityFunc(c, e) + dist := pos.Sub(c.Pos).Len() + if dist > d { + continue + } + if explodable, ok := e.(ExplodableEntity); ok { + explodable.Explode(c, (1-dist/d)*exposure(c.Pos, e)) } } - var affectedBlocks []cube.Pos + var affectedBlocks = make([]cube.Pos, 0, 32) for _, ray := range rays { pos := c.Pos for blastForce := c.Size * (0.7 + r.Float64()*0.6); blastForce > 0.0; blastForce -= 0.225 { current := cube.PosFromVec3(pos) - bl := c.World.Block(current) - if r, ok := bl.(interface{ BlastResistance() float64 }); ok { + if r, ok := c.World.Block(current).(interface{ BlastResistance() float64 }); ok { if blastForce -= (r.BlastResistance() + 0.3) * 0.3; blastForce > 0 { affectedBlocks = append(affectedBlocks, current) } @@ -115,11 +88,45 @@ func (c Config) Do() { } } for _, pos := range affectedBlocks { - c.BlockFunc(c, pos) + if explodable, ok := c.World.Block(pos).(ExplodableBlock); ok { + explodable.Explode(pos, c) + } + } + if c.Fire { + f := fire() + for _, pos := range affectedBlocks { + if rand.Intn(3) == 0 { + if c.World.Block(pos) == air() { + c.World.SetBlock(pos, f, nil) + } + } + } + } + + if c.Particle != nil { + c.World.AddParticle(c.Pos, c.Particle) + } + if c.Sound != nil { + c.World.PlaySound(c.Pos, c.Sound) } +} - c.World.AddParticle(c.Pos, particle.HugeExplosion{}) - c.World.PlaySound(c.Pos, sound.Explosion{}) +// fire returns a fire block. +func fire() world.Block { + f, ok := world.BlockByName("minecraft:fire", map[string]any{"age": int32(0)}) + if !ok { + panic("could not find fire block") + } + return f +} + +// air returns a air block. +func air() world.Block { + f, ok := world.BlockByName("minecraft:air", map[string]any{}) + if !ok { + panic("could not find air block") + } + return f } // TODO @@ -149,9 +156,9 @@ func exposure(origin mgl64.Vec3, e world.Entity) float64 { } var collides bool trace.TraverseBlocks(dck2, origin, func(pos cube.Pos) (con bool) { - _, ok := w.Block(pos).(block.Air) - collides = !ok - return ok + air := w.Block(pos) == air() + collides = !air + return air }) if collides { integer14++ From 11882b2721c6dd1562f6f62b1b4a1ca4dc707ca4 Mon Sep 17 00:00:00 2001 From: T14Raptor Date: Thu, 14 Jul 2022 19:52:48 -0400 Subject: [PATCH 03/41] Moved explosions to block package --- main.go | 19 +--- server/block/bedrock.go | 14 +-- server/block/block.go | 98 ++++++++++++++++--- server/block/break_info.go | 8 ++ server/block/cactus.go | 3 +- server/block/crop.go | 7 +- server/block/dirt.go | 14 +-- server/block/double_flower.go | 6 +- .../{world/explosion => block}/explosion.go | 53 ++++------ server/block/farmland.go | 3 +- server/block/fire.go | 5 +- server/block/grass.go | 13 +-- server/block/item_frame.go | 5 +- server/block/lava.go | 5 +- server/block/liquid.go | 7 +- server/block/water.go | 3 +- server/entity/arrow.go | 4 +- server/entity/ender_pearl.go | 4 +- server/entity/experience_orb.go | 4 +- server/entity/falling_block.go | 10 +- server/entity/item.go | 12 ++- server/entity/snowball.go | 4 +- server/entity/splash_potion.go | 4 +- 23 files changed, 157 insertions(+), 148 deletions(-) rename server/{world/explosion => block}/explosion.go (77%) diff --git a/main.go b/main.go index 83f5016d1..395aa5d98 100644 --- a/main.go +++ b/main.go @@ -3,17 +3,15 @@ package main import ( "fmt" "github.com/df-mc/dragonfly/server" + "github.com/df-mc/dragonfly/server/block" "github.com/df-mc/dragonfly/server/event" "github.com/df-mc/dragonfly/server/player" "github.com/df-mc/dragonfly/server/player/chat" "github.com/df-mc/dragonfly/server/world" - "github.com/df-mc/dragonfly/server/world/explosion" "github.com/pelletier/go-toml" "github.com/sirupsen/logrus" "io/ioutil" "os" - "runtime/pprof" - "time" ) func main() { @@ -34,19 +32,6 @@ func main() { log.Fatalln(err) } - go func() { - time.Sleep(5 * time.Second) - - f, err := os.Create("mem.pprof") - if err != nil { - panic(err) - } - pprof.WriteHeapProfile(f) - f.Close() - - fmt.Println("done") - }() - for srv.Accept(func(p *player.Player) { p.SetGameMode(world.GameModeSurvival) p.Handle(h{p: p}) @@ -61,7 +46,7 @@ type h struct { } func (h h) HandleChat(*event.Context, *string) { - explosion.Config{ + block.ExplosionConfig{ World: h.p.World(), Pos: h.p.Position(), Size: 4, diff --git a/server/block/bedrock.go b/server/block/bedrock.go index 35ef77882..a82a5ca63 100644 --- a/server/block/bedrock.go +++ b/server/block/bedrock.go @@ -1,10 +1,5 @@ package block -import ( - "github.com/df-mc/dragonfly/server/block/cube" - "github.com/df-mc/dragonfly/server/world/explosion" -) - // Bedrock is a block that is indestructible in survival. type Bedrock struct { solid @@ -16,14 +11,11 @@ type Bedrock struct { InfiniteBurning bool } -// BlastResistance ... -func (Bedrock) BlastResistance() float64 { - return 3600000 +// BreakInfo ... +func (b Bedrock) BreakInfo() BreakInfo { + return newBreakInfo(-1, neverHarvestable, nothingEffective, simpleDrops()).withBlastResistance(0.6) } -// Explode ... -func (b Bedrock) Explode(cube.Pos, explosion.Config) {} - // EncodeItem ... func (Bedrock) EncodeItem() (name string, meta int16) { return "minecraft:bedrock", 0 diff --git a/server/block/block.go b/server/block/block.go index 549d965a9..71a01d883 100644 --- a/server/block/block.go +++ b/server/block/block.go @@ -2,13 +2,15 @@ package block import ( "github.com/df-mc/dragonfly/server/block/cube" - "github.com/df-mc/dragonfly/server/entity" + "github.com/df-mc/dragonfly/server/entity/damage" + "github.com/df-mc/dragonfly/server/entity/effect" + "github.com/df-mc/dragonfly/server/entity/healing" "github.com/df-mc/dragonfly/server/item" "github.com/df-mc/dragonfly/server/world" - "github.com/df-mc/dragonfly/server/world/explosion" "github.com/df-mc/dragonfly/server/world/sound" "github.com/go-gl/mathgl/mgl64" "math/rand" + "time" ) // Activatable represents a block that may be activated by a viewer of the world. When activated, the block @@ -85,7 +87,7 @@ func calculateFace(user item.User, placePos cube.Pos) cube.Face { pos := cube.PosFromVec3(userPos) if abs(pos[0]-placePos[0]) < 2 && abs(pos[2]-placePos[2]) < 2 { y := userPos[1] - if eyed, ok := user.(entity.Eyed); ok { + if eyed, ok := user.(interface{ EyeHeight() float64 }); ok { y += eyed.EyeHeight() } @@ -204,21 +206,16 @@ func (g gravityAffected) fall(b world.Block, pos cube.Pos, w *world.World) { _, liquid := w.Liquid(pos.Side(cube.FaceDown)) if air || liquid { w.SetBlock(pos, nil, nil) - w.AddEntity(entity.NewFallingBlock(b, pos.Vec3Middle())) - } -} -type explodable struct { - b BreakInfo -} + ent, ok := world.EntityByName("minecraft:falling_block") + if !ok { + return + } -func (e explodable) Explode(pos cube.Pos, c explosion.Config) { - c.World.SetBlock(pos, nil, nil) - if 1/c.Size > rand.Float64() { - for _, drop := range e.b.Drops(item.ToolNone{}, nil) { - it := entity.NewItem(drop, pos.Vec3Centre()) - it.SetVelocity(mgl64.Vec3{rand.Float64()*0.2 - 1, 0.2, rand.Float64()*0.2 - 1}) - c.World.AddEntity(entity.NewItem(drop, pos.Vec3Centre())) + if p, ok := ent.(interface { + New(bl world.Block, pos mgl64.Vec3) world.Entity + }); ok { + w.AddEntity(p.New(b, pos.Vec3Centre())) } } } @@ -248,6 +245,75 @@ func newFlammabilityInfo(encouragement, flammability int, lavaFlammable bool) Fl } } +type livingEntity interface { + world.Entity + // Health returns the health of the entity. + Health() float64 + // MaxHealth returns the maximum health of the entity. + MaxHealth() float64 + // SetMaxHealth changes the maximum health of the entity to the value passed. + SetMaxHealth(v float64) + // Dead checks if the entity is considered dead. True is returned if the health of the entity is equal to or + // lower than 0. + Dead() bool + // AttackImmune checks if the entity is currently immune to entity attacks. Entities typically turn + // immune for half a second after being attacked. + AttackImmune() bool + // Hurt hurts the entity for a given amount of damage. The source passed represents the cause of the + // damage, for example damage.SourceEntityAttack if the entity is attacked by another entity. + // If the final damage exceeds the health that the entity currently has, the entity is killed. + // Hurt returns the final amount of damage dealt to the Living entity and returns whether the Living entity + // was vulnerable to the damage at all. + Hurt(damage float64, src damage.Source) (n float64, vulnerable bool) + // Heal heals the entity for a given amount of health. The source passed represents the cause of the + // healing, for example healing.SourceFood if the entity healed by having a full food bar. If the health + // added to the original health exceeds the entity's max health, Heal may not add the full amount. + Heal(health float64, src healing.Source) + // KnockBack knocks the entity back with a given force and height. A source is passed which indicates the + // source of the velocity, typically the position of an attacking entity. The source is used to calculate + // the direction which the entity should be knocked back in. + KnockBack(src mgl64.Vec3, force, height float64) + // AddEffect adds an entity.Effect to the entity. If the effect is instant, it is applied to the entity + // immediately. If not, the effect is applied to the entity every time the Tick method is called. + // AddEffect will overwrite any effects present if the level of the effect is higher than the existing one, or + // if the effects' levels are equal and the new effect has a longer duration. + AddEffect(e effect.Effect) + // RemoveEffect removes any effect that might currently be active on the entity. + RemoveEffect(e effect.Type) + // Effects returns any effect currently applied to the entity. The returned effects are guaranteed not to have + // expired when returned. + Effects() []effect.Effect + // Speed returns the current speed of the living entity. The default value is different for each entity. + Speed() float64 + // SetSpeed sets the speed of an entity to a new value. + SetSpeed(float64) +} + +type flammableEntity interface { + // FireProof is whether the entity is currently fireproof. + FireProof() bool + // OnFireDuration returns duration of fire in ticks. + OnFireDuration() time.Duration + // SetOnFire sets the entity on fire for the specified duration. + SetOnFire(duration time.Duration) + // Extinguish extinguishes the entity. + Extinguish() +} + +// dropItem ... +func dropItem(w *world.World, it item.Stack, pos mgl64.Vec3) { + ent, ok := world.EntityByName("minecraft:item") + if !ok { + return + } + + if p, ok := ent.(interface { + New(it item.Stack, pos, vel mgl64.Vec3) world.Entity + }); ok { + w.AddEntity(p.New(it, pos, mgl64.Vec3{rand.Float64()*0.2 - 0.1, 0.2, rand.Float64()*0.2 - 0.1})) + } +} + // bass is a struct that may be embedded for blocks that create a bass sound. type bass struct{} diff --git a/server/block/break_info.go b/server/block/break_info.go index 189d815d9..e81bd2116 100644 --- a/server/block/break_info.go +++ b/server/block/break_info.go @@ -85,6 +85,8 @@ type BreakInfo struct { Drops func(t item.Tool, enchantments []item.Enchantment) []item.Stack // XPDrops is the range of XP a block can drop when broken. XPDrops XPDropRange + // BlastResistance ... + BlastResistance float64 } // newBreakInfo creates a BreakInfo struct with the properties passed. The XPDrops field is 0 by default. @@ -103,6 +105,12 @@ func (b BreakInfo) withXPDropRange(min, max int) BreakInfo { return b } +// withBlastResistance sets the BlastResistance field of the BreakInfo struct to the passed value. +func (b BreakInfo) withBlastResistance(res float64) BreakInfo { + b.BlastResistance = res + return b +} + // XPDropRange holds the min & max XP drop amounts of blocks. type XPDropRange [2]int diff --git a/server/block/cactus.go b/server/block/cactus.go index 1a5be8f15..295cab1dd 100644 --- a/server/block/cactus.go +++ b/server/block/cactus.go @@ -3,7 +3,6 @@ package block import ( "github.com/df-mc/dragonfly/server/block/cube" "github.com/df-mc/dragonfly/server/block/model" - "github.com/df-mc/dragonfly/server/entity" "github.com/df-mc/dragonfly/server/entity/damage" "github.com/df-mc/dragonfly/server/item" "github.com/df-mc/dragonfly/server/world" @@ -78,7 +77,7 @@ func (c Cactus) RandomTick(pos cube.Pos, w *world.World, r *rand.Rand) { // EntityInside ... func (c Cactus) EntityInside(_ cube.Pos, _ *world.World, e world.Entity) { - if l, ok := e.(entity.Living); ok && !l.AttackImmune() { + if l, ok := e.(livingEntity); ok && !l.AttackImmune() { l.Hurt(0.5, damage.SourceBlock{Block: c}) } } diff --git a/server/block/crop.go b/server/block/crop.go index 6f7407c27..26de4361f 100644 --- a/server/block/crop.go +++ b/server/block/crop.go @@ -2,11 +2,8 @@ package block import ( "github.com/df-mc/dragonfly/server/block/cube" - "github.com/df-mc/dragonfly/server/entity" "github.com/df-mc/dragonfly/server/item" "github.com/df-mc/dragonfly/server/world" - "github.com/go-gl/mathgl/mgl64" - "math/rand" ) // Crop is an interface for all crops that are grown on farmland. A crop has a random chance to grow during random ticks. @@ -33,9 +30,7 @@ func (c crop) NeighbourUpdateTick(pos, _ cube.Pos, w *world.World) { w.SetBlock(pos, nil, nil) if breakable, ok := b.(Breakable); ok { for _, drop := range breakable.BreakInfo().Drops(item.ToolNone{}, []item.Enchantment{}) { - itemEntity := entity.NewItem(drop, pos.Vec3Centre()) - itemEntity.SetVelocity(mgl64.Vec3{rand.Float64()*0.2 - 0.1, 0.2, rand.Float64()*0.2 - 0.1}) - w.AddEntity(itemEntity) + dropItem(w, drop, pos.Vec3Centre()) } } } diff --git a/server/block/dirt.go b/server/block/dirt.go index 377d0fd6b..ab1d0cf25 100644 --- a/server/block/dirt.go +++ b/server/block/dirt.go @@ -1,9 +1,7 @@ package block import ( - "github.com/df-mc/dragonfly/server/block/cube" "github.com/df-mc/dragonfly/server/world" - "github.com/df-mc/dragonfly/server/world/explosion" ) // Dirt is a block found abundantly in most biomes under a layer of grass blocks at the top of the normal @@ -27,7 +25,7 @@ func (d Dirt) SoilFor(block world.Block) bool { // BreakInfo ... func (d Dirt) BreakInfo() BreakInfo { - return newBreakInfo(0.5, alwaysHarvestable, shovelEffective, oneOf(d)) + return newBreakInfo(0.5, alwaysHarvestable, shovelEffective, oneOf(d)).withBlastResistance(0.5) } // Till ... @@ -43,16 +41,6 @@ func (d Dirt) Shovel() (world.Block, bool) { return DirtPath{}, true } -// BlastResistance ... -func (Dirt) BlastResistance() float64 { - return 0.5 -} - -// Explode ... -func (d Dirt) Explode(pos cube.Pos, c explosion.Config) { - explodable{b: d.BreakInfo()}.Explode(pos, c) -} - // EncodeItem ... func (d Dirt) EncodeItem() (name string, meta int16) { if d.Coarse { diff --git a/server/block/double_flower.go b/server/block/double_flower.go index e1a8e16e1..11416b058 100644 --- a/server/block/double_flower.go +++ b/server/block/double_flower.go @@ -2,12 +2,10 @@ package block import ( "github.com/df-mc/dragonfly/server/block/cube" - "github.com/df-mc/dragonfly/server/entity" "github.com/df-mc/dragonfly/server/item" "github.com/df-mc/dragonfly/server/world" "github.com/df-mc/dragonfly/server/world/particle" "github.com/go-gl/mathgl/mgl64" - "math/rand" ) // DoubleFlower is a two block high flower consisting of an upper and lower part. @@ -28,9 +26,7 @@ func (d DoubleFlower) FlammabilityInfo() FlammabilityInfo { // BoneMeal ... func (d DoubleFlower) BoneMeal(pos cube.Pos, w *world.World) bool { - itemEntity := entity.NewItem(item.NewStack(d, 1), pos.Vec3Centre()) - itemEntity.SetVelocity(mgl64.Vec3{rand.Float64()*0.2 - 0.1, 0.2, rand.Float64()*0.2 - 0.1}) - w.AddEntity(itemEntity) + dropItem(w, item.NewStack(d, 1), pos.Vec3Centre()) return true } diff --git a/server/world/explosion/explosion.go b/server/block/explosion.go similarity index 77% rename from server/world/explosion/explosion.go rename to server/block/explosion.go index 1dfdd2ab2..461db15a6 100644 --- a/server/world/explosion/explosion.go +++ b/server/block/explosion.go @@ -1,8 +1,9 @@ -package explosion +package block import ( "github.com/df-mc/dragonfly/server/block/cube" "github.com/df-mc/dragonfly/server/block/cube/trace" + "github.com/df-mc/dragonfly/server/item" "github.com/df-mc/dragonfly/server/world" "github.com/go-gl/mathgl/mgl64" "math" @@ -26,15 +27,14 @@ func init() { } type ExplodableEntity interface { - Explode(c Config, impact float64) + Explode(c ExplosionConfig, impact float64) } -type ExplodableBlock interface { - BlastResistance() float64 - Explode(pos cube.Pos, c Config) +type Explodable interface { + Explode(pos cube.Pos, c ExplosionConfig) } -type Config struct { +type ExplosionConfig struct { World *world.World Pos mgl64.Vec3 Size float64 @@ -45,7 +45,7 @@ type Config struct { Particle world.Particle } -func (c Config) Do() { +func (c ExplosionConfig) Do() { if c.Rand == nil { c.Rand = rand.NewSource(time.Now().UnixNano()) } @@ -79,8 +79,8 @@ func (c Config) Do() { pos := c.Pos for blastForce := c.Size * (0.7 + r.Float64()*0.6); blastForce > 0.0; blastForce -= 0.225 { current := cube.PosFromVec3(pos) - if r, ok := c.World.Block(current).(interface{ BlastResistance() float64 }); ok { - if blastForce -= (r.BlastResistance() + 0.3) * 0.3; blastForce > 0 { + if r, ok := c.World.Block(current).(Breakable); ok { + if blastForce -= (r.BreakInfo().BlastResistance + 0.3) * 0.3; blastForce > 0 { affectedBlocks = append(affectedBlocks, current) } } @@ -88,16 +88,23 @@ func (c Config) Do() { } } for _, pos := range affectedBlocks { - if explodable, ok := c.World.Block(pos).(ExplodableBlock); ok { + bl := c.World.Block(pos) + if explodable, ok := bl.(Explodable); ok { explodable.Explode(pos, c) + } else if breakable, ok := bl.(Breakable); ok { + c.World.SetBlock(pos, nil, nil) + if 1/c.Size > rand.Float64() { + for _, drop := range breakable.BreakInfo().Drops(item.ToolNone{}, nil) { + dropItem(c.World, drop, pos.Vec3Centre()) + } + } } } if c.Fire { - f := fire() for _, pos := range affectedBlocks { if rand.Intn(3) == 0 { - if c.World.Block(pos) == air() { - c.World.SetBlock(pos, f, nil) + if _, ok := c.World.Block(pos).(Air); ok { + c.World.SetBlock(pos, Fire{}, nil) } } } @@ -111,24 +118,6 @@ func (c Config) Do() { } } -// fire returns a fire block. -func fire() world.Block { - f, ok := world.BlockByName("minecraft:fire", map[string]any{"age": int32(0)}) - if !ok { - panic("could not find fire block") - } - return f -} - -// air returns a air block. -func air() world.Block { - f, ok := world.BlockByName("minecraft:air", map[string]any{}) - if !ok { - panic("could not find air block") - } - return f -} - // TODO func exposure(origin mgl64.Vec3, e world.Entity) float64 { w := e.World() @@ -156,7 +145,7 @@ func exposure(origin mgl64.Vec3, e world.Entity) float64 { } var collides bool trace.TraverseBlocks(dck2, origin, func(pos cube.Pos) (con bool) { - air := w.Block(pos) == air() + _, air := w.Block(pos).(Air) collides = !air return air }) diff --git a/server/block/farmland.go b/server/block/farmland.go index d500ea2f6..54c2027ce 100644 --- a/server/block/farmland.go +++ b/server/block/farmland.go @@ -2,7 +2,6 @@ package block import ( "github.com/df-mc/dragonfly/server/block/cube" - "github.com/df-mc/dragonfly/server/entity" "github.com/df-mc/dragonfly/server/world" "math/rand" ) @@ -71,7 +70,7 @@ func (f Farmland) hydrated(pos cube.Pos, w *world.World) bool { // EntityLand ... func (f Farmland) EntityLand(pos cube.Pos, w *world.World, e world.Entity, distance *float64) { - if living, ok := e.(entity.Living); ok { + if living, ok := e.(livingEntity); ok { if fall, ok := living.(fallDistanceEntity); ok && rand.Float64() < fall.FallDistance()-0.5 { w.SetBlock(pos, Dirt{}, nil) } diff --git a/server/block/fire.go b/server/block/fire.go index ffc8be154..fccfec943 100644 --- a/server/block/fire.go +++ b/server/block/fire.go @@ -4,7 +4,6 @@ package block import ( "github.com/df-mc/dragonfly/server/block/cube" - "github.com/df-mc/dragonfly/server/entity" "github.com/df-mc/dragonfly/server/entity/damage" "github.com/df-mc/dragonfly/server/event" "github.com/df-mc/dragonfly/server/world" @@ -188,8 +187,8 @@ func (f Fire) spread(from, to cube.Pos, w *world.World, r *rand.Rand) { // EntityInside ... func (f Fire) EntityInside(_ cube.Pos, _ *world.World, e world.Entity) { - if flammable, ok := e.(entity.Flammable); ok { - if l, ok := e.(entity.Living); ok && !l.AttackImmune() { + if flammable, ok := e.(flammableEntity); ok { + if l, ok := e.(livingEntity); ok && !l.AttackImmune() { l.Hurt(f.Type.Damage(), damage.SourceFire{}) } if flammable.OnFireDuration() < time.Second*8 { diff --git a/server/block/grass.go b/server/block/grass.go index ab2682586..c2289ab35 100644 --- a/server/block/grass.go +++ b/server/block/grass.go @@ -3,7 +3,6 @@ package block import ( "github.com/df-mc/dragonfly/server/block/cube" "github.com/df-mc/dragonfly/server/world" - "github.com/df-mc/dragonfly/server/world/explosion" "math/rand" ) @@ -44,16 +43,6 @@ func (g Grass) SoilFor(block world.Block) bool { return false } -// BlastResistance ... -func (g Grass) BlastResistance() float64 { - return 0.6 -} - -// Explode ... -func (g Grass) Explode(pos cube.Pos, c explosion.Config) { - explodable{b: g.BreakInfo()}.Explode(pos, c) -} - // RandomTick handles the ticking of grass, which may or may not result in the spreading of grass onto dirt. func (g Grass) RandomTick(pos cube.Pos, w *world.World, r *rand.Rand) { aboveLight := w.Light(pos.Add(cube.Pos{0, 1})) @@ -103,7 +92,7 @@ func (g Grass) BoneMeal(pos cube.Pos, w *world.World) bool { // BreakInfo ... func (g Grass) BreakInfo() BreakInfo { - return newBreakInfo(0.6, alwaysHarvestable, shovelEffective, silkTouchOneOf(Dirt{}, g)) + return newBreakInfo(0.6, alwaysHarvestable, shovelEffective, silkTouchOneOf(Dirt{}, g)).withBlastResistance(0.6) } // EncodeItem ... diff --git a/server/block/item_frame.go b/server/block/item_frame.go index 2a383412b..c3a57c832 100644 --- a/server/block/item_frame.go +++ b/server/block/item_frame.go @@ -3,7 +3,6 @@ package block import ( "github.com/df-mc/dragonfly/server/block/cube" "github.com/df-mc/dragonfly/server/block/model" - "github.com/df-mc/dragonfly/server/entity" "github.com/df-mc/dragonfly/server/internal/nbtconv" "github.com/df-mc/dragonfly/server/item" "github.com/df-mc/dragonfly/server/world" @@ -60,9 +59,7 @@ func (i ItemFrame) Punch(pos cube.Pos, _ cube.Face, w *world.World, u item.User) GameMode() world.GameMode }); ok { if rand.Float64() <= i.DropChance && !g.GameMode().CreativeInventory() { - it := entity.NewItem(i.Item, pos.Vec3Centre()) - it.SetVelocity(mgl64.Vec3{rand.Float64()*0.2 - 0.1, 0.2, rand.Float64()*0.2 - 0.1}) - w.AddEntity(it) + dropItem(w, i.Item, pos.Vec3Centre()) } } i.Item, i.Rotations = item.Stack{}, 0 diff --git a/server/block/lava.go b/server/block/lava.go index acf565cbb..f62c2154c 100644 --- a/server/block/lava.go +++ b/server/block/lava.go @@ -2,7 +2,6 @@ package block import ( "github.com/df-mc/dragonfly/server/block/cube" - "github.com/df-mc/dragonfly/server/entity" "github.com/df-mc/dragonfly/server/entity/damage" "github.com/df-mc/dragonfly/server/event" "github.com/df-mc/dragonfly/server/world" @@ -42,8 +41,8 @@ func (l Lava) EntityInside(_ cube.Pos, _ *world.World, e world.Entity) { if fallEntity, ok := e.(fallDistanceEntity); ok { fallEntity.ResetFallDistance() } - if flammable, ok := e.(entity.Flammable); ok { - if l, ok := e.(entity.Living); ok && !l.AttackImmune() { + if flammable, ok := e.(flammableEntity); ok { + if l, ok := e.(livingEntity); ok && !l.AttackImmune() { l.Hurt(4, damage.SourceLava{}) } flammable.SetOnFire(15 * time.Second) diff --git a/server/block/liquid.go b/server/block/liquid.go index 031182a42..8bd994ac4 100644 --- a/server/block/liquid.go +++ b/server/block/liquid.go @@ -2,13 +2,10 @@ package block import ( "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/world" - "github.com/go-gl/mathgl/mgl64" "math" - "math/rand" "sync" ) @@ -146,9 +143,7 @@ func flowInto(b world.Liquid, src, pos cube.Pos, w *world.World, falling bool) b if removable.HasLiquidDrops() { if b, ok := existing.(Breakable); ok { for _, d := range b.BreakInfo().Drops(item.ToolNone{}, nil) { - itemEntity := entity.NewItem(d, pos.Vec3Centre()) - itemEntity.SetVelocity(mgl64.Vec3{rand.Float64()*0.2 - 0.1, 0.2, rand.Float64()*0.2 - 0.1}) - w.AddEntity(itemEntity) + dropItem(w, d, pos.Vec3Centre()) } } else { panic("liquid drops should always implement breakable") diff --git a/server/block/water.go b/server/block/water.go index 316ec1f2f..caa2c3474 100644 --- a/server/block/water.go +++ b/server/block/water.go @@ -2,7 +2,6 @@ package block import ( "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/potion" @@ -32,7 +31,7 @@ func (w Water) EntityInside(_ cube.Pos, _ *world.World, e world.Entity) { if fallEntity, ok := e.(fallDistanceEntity); ok { fallEntity.ResetFallDistance() } - if flammable, ok := e.(entity.Flammable); ok { + if flammable, ok := e.(flammableEntity); ok { flammable.Extinguish() } } diff --git a/server/entity/arrow.go b/server/entity/arrow.go index b165e401e..370297b2d 100644 --- a/server/entity/arrow.go +++ b/server/entity/arrow.go @@ -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/block/cube/trace" "github.com/df-mc/dragonfly/server/entity/damage" @@ -8,7 +9,6 @@ import ( "github.com/df-mc/dragonfly/server/item" "github.com/df-mc/dragonfly/server/item/potion" "github.com/df-mc/dragonfly/server/world" - "github.com/df-mc/dragonfly/server/world/explosion" "github.com/df-mc/dragonfly/server/world/sound" "github.com/go-gl/mathgl/mgl64" "math" @@ -234,7 +234,7 @@ func (a *Arrow) New(pos, vel mgl64.Vec3, yaw, pitch float64, owner world.Entity, } // Explode ... -func (a *Arrow) Explode(c explosion.Config, impact float64) { +func (a *Arrow) Explode(c block.ExplosionConfig, impact float64) { a.SetVelocity(a.Position().Sub(c.Pos).Normalize().Mul(impact)) } diff --git a/server/entity/ender_pearl.go b/server/entity/ender_pearl.go index 7a9726562..1864b8d1b 100644 --- a/server/entity/ender_pearl.go +++ b/server/entity/ender_pearl.go @@ -1,12 +1,12 @@ package entity import ( + "github.com/df-mc/dragonfly/server/block" "github.com/df-mc/dragonfly/server/block/cube" "github.com/df-mc/dragonfly/server/block/cube/trace" "github.com/df-mc/dragonfly/server/entity/damage" "github.com/df-mc/dragonfly/server/internal/nbtconv" "github.com/df-mc/dragonfly/server/world" - "github.com/df-mc/dragonfly/server/world/explosion" "github.com/df-mc/dragonfly/server/world/particle" "github.com/df-mc/dragonfly/server/world/sound" "github.com/go-gl/mathgl/mgl64" @@ -133,7 +133,7 @@ func (e *EnderPearl) New(pos, vel mgl64.Vec3, yaw, pitch float64) world.Entity { } // Explode ... -func (e *EnderPearl) Explode(c explosion.Config, impact float64) { +func (e *EnderPearl) Explode(c block.ExplosionConfig, impact float64) { e.SetVelocity(e.Position().Sub(c.Pos).Normalize().Mul(impact)) } diff --git a/server/entity/experience_orb.go b/server/entity/experience_orb.go index abd75fc5c..b8b509bae 100644 --- a/server/entity/experience_orb.go +++ b/server/entity/experience_orb.go @@ -1,10 +1,10 @@ package entity import ( + "github.com/df-mc/dragonfly/server/block" "github.com/df-mc/dragonfly/server/block/cube" "github.com/df-mc/dragonfly/server/internal/nbtconv" "github.com/df-mc/dragonfly/server/world" - "github.com/df-mc/dragonfly/server/world/explosion" "github.com/go-gl/mathgl/mgl64" "golang.org/x/exp/slices" "math" @@ -138,7 +138,7 @@ func (e *ExperienceOrb) Tick(w *world.World, current int64) { } // Explode ... -func (e *ExperienceOrb) Explode(explosion.Config, float64) { +func (e *ExperienceOrb) Explode(block.ExplosionConfig, float64) { _ = e.Close() } diff --git a/server/entity/falling_block.go b/server/entity/falling_block.go index 338d326eb..014895752 100644 --- a/server/entity/falling_block.go +++ b/server/entity/falling_block.go @@ -3,12 +3,12 @@ package entity import ( "fmt" "github.com/df-mc/atomic" + "github.com/df-mc/dragonfly/server/block" "github.com/df-mc/dragonfly/server/block/cube" "github.com/df-mc/dragonfly/server/entity/damage" "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/explosion" "github.com/go-gl/mathgl/mgl64" "math" "math/rand" @@ -130,8 +130,14 @@ func (f *FallingBlock) Tick(w *world.World, _ int64) { } } +// New creates and returns an FallingBlock with the world.Block and position provided. It doesn't spawn the FallingBlock +// by itself. +func (f *FallingBlock) New(bl world.Block, pos mgl64.Vec3) world.Entity { + return NewFallingBlock(bl, pos) +} + // Explode ... -func (f *FallingBlock) Explode(explosion.Config, float64) { +func (f *FallingBlock) Explode(block.ExplosionConfig, float64) { _ = f.Close() } diff --git a/server/entity/item.go b/server/entity/item.go index 32935b093..2f4628a07 100644 --- a/server/entity/item.go +++ b/server/entity/item.go @@ -2,11 +2,11 @@ package entity import ( "fmt" + "github.com/df-mc/dragonfly/server/block" "github.com/df-mc/dragonfly/server/block/cube" "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/explosion" "github.com/go-gl/mathgl/mgl64" "math" "time" @@ -167,8 +167,16 @@ func (it *Item) collect(w *world.World, collector Collector, pos mgl64.Vec3) { _ = it.Close() } +// New creates and returns an Item with the item.Stack, position, and velocity provided. It doesn't spawn the Item +// by itself. +func (it *Item) New(stack item.Stack, pos, vel mgl64.Vec3) world.Entity { + itemEntity := NewItem(stack, pos) + itemEntity.vel = vel + return itemEntity +} + // Explode ... -func (it *Item) Explode(explosion.Config, float64) { +func (it *Item) Explode(block.ExplosionConfig, float64) { _ = it.Close() } diff --git a/server/entity/snowball.go b/server/entity/snowball.go index dbeb3fb60..817411d62 100644 --- a/server/entity/snowball.go +++ b/server/entity/snowball.go @@ -1,12 +1,12 @@ package entity import ( + "github.com/df-mc/dragonfly/server/block" "github.com/df-mc/dragonfly/server/block/cube" "github.com/df-mc/dragonfly/server/block/cube/trace" "github.com/df-mc/dragonfly/server/entity/damage" "github.com/df-mc/dragonfly/server/internal/nbtconv" "github.com/df-mc/dragonfly/server/world" - "github.com/df-mc/dragonfly/server/world/explosion" "github.com/df-mc/dragonfly/server/world/particle" "github.com/go-gl/mathgl/mgl64" ) @@ -114,7 +114,7 @@ func (s *Snowball) New(pos, vel mgl64.Vec3, yaw, pitch float64) world.Entity { } // Explode ... -func (s *Snowball) Explode(c explosion.Config, impact float64) { +func (s *Snowball) Explode(c block.ExplosionConfig, impact float64) { s.SetVelocity(s.Position().Sub(c.Pos).Normalize().Mul(impact)) } diff --git a/server/entity/splash_potion.go b/server/entity/splash_potion.go index 0dd8b12e8..f0fc5ec06 100644 --- a/server/entity/splash_potion.go +++ b/server/entity/splash_potion.go @@ -1,13 +1,13 @@ package entity import ( + "github.com/df-mc/dragonfly/server/block" "github.com/df-mc/dragonfly/server/block/cube" "github.com/df-mc/dragonfly/server/block/cube/trace" "github.com/df-mc/dragonfly/server/entity/effect" "github.com/df-mc/dragonfly/server/internal/nbtconv" "github.com/df-mc/dragonfly/server/item/potion" "github.com/df-mc/dragonfly/server/world" - "github.com/df-mc/dragonfly/server/world/explosion" "github.com/df-mc/dragonfly/server/world/particle" "github.com/df-mc/dragonfly/server/world/sound" "github.com/go-gl/mathgl/mgl64" @@ -178,7 +178,7 @@ func (s *SplashPotion) New(pos, vel mgl64.Vec3, yaw, pitch float64, t potion.Pot } // Explode ... -func (s *SplashPotion) Explode(c explosion.Config, impact float64) { +func (s *SplashPotion) Explode(c block.ExplosionConfig, impact float64) { s.SetVelocity(s.Position().Sub(c.Pos).Normalize().Mul(impact)) } From c8c1965b5da09a1cebe05c050e163556b09a0738 Mon Sep 17 00:00:00 2001 From: T14Raptor Date: Fri, 15 Jul 2022 15:47:47 -0400 Subject: [PATCH 04/41] improvements --- server/block/block.go | 36 ----------------- server/block/break_info.go | 3 +- server/block/explosion.go | 83 +++++++++++++++++++++----------------- 3 files changed, 49 insertions(+), 73 deletions(-) diff --git a/server/block/block.go b/server/block/block.go index 71a01d883..6f975d7c1 100644 --- a/server/block/block.go +++ b/server/block/block.go @@ -3,8 +3,6 @@ package block import ( "github.com/df-mc/dragonfly/server/block/cube" "github.com/df-mc/dragonfly/server/entity/damage" - "github.com/df-mc/dragonfly/server/entity/effect" - "github.com/df-mc/dragonfly/server/entity/healing" "github.com/df-mc/dragonfly/server/item" "github.com/df-mc/dragonfly/server/world" "github.com/df-mc/dragonfly/server/world/sound" @@ -246,16 +244,6 @@ func newFlammabilityInfo(encouragement, flammability int, lavaFlammable bool) Fl } type livingEntity interface { - world.Entity - // Health returns the health of the entity. - Health() float64 - // MaxHealth returns the maximum health of the entity. - MaxHealth() float64 - // SetMaxHealth changes the maximum health of the entity to the value passed. - SetMaxHealth(v float64) - // Dead checks if the entity is considered dead. True is returned if the health of the entity is equal to or - // lower than 0. - Dead() bool // AttackImmune checks if the entity is currently immune to entity attacks. Entities typically turn // immune for half a second after being attacked. AttackImmune() bool @@ -265,33 +253,9 @@ type livingEntity interface { // Hurt returns the final amount of damage dealt to the Living entity and returns whether the Living entity // was vulnerable to the damage at all. Hurt(damage float64, src damage.Source) (n float64, vulnerable bool) - // Heal heals the entity for a given amount of health. The source passed represents the cause of the - // healing, for example healing.SourceFood if the entity healed by having a full food bar. If the health - // added to the original health exceeds the entity's max health, Heal may not add the full amount. - Heal(health float64, src healing.Source) - // KnockBack knocks the entity back with a given force and height. A source is passed which indicates the - // source of the velocity, typically the position of an attacking entity. The source is used to calculate - // the direction which the entity should be knocked back in. - KnockBack(src mgl64.Vec3, force, height float64) - // AddEffect adds an entity.Effect to the entity. If the effect is instant, it is applied to the entity - // immediately. If not, the effect is applied to the entity every time the Tick method is called. - // AddEffect will overwrite any effects present if the level of the effect is higher than the existing one, or - // if the effects' levels are equal and the new effect has a longer duration. - AddEffect(e effect.Effect) - // RemoveEffect removes any effect that might currently be active on the entity. - RemoveEffect(e effect.Type) - // Effects returns any effect currently applied to the entity. The returned effects are guaranteed not to have - // expired when returned. - Effects() []effect.Effect - // Speed returns the current speed of the living entity. The default value is different for each entity. - Speed() float64 - // SetSpeed sets the speed of an entity to a new value. - SetSpeed(float64) } type flammableEntity interface { - // FireProof is whether the entity is currently fireproof. - FireProof() bool // OnFireDuration returns duration of fire in ticks. OnFireDuration() time.Duration // SetOnFire sets the entity on fire for the specified duration. diff --git a/server/block/break_info.go b/server/block/break_info.go index e81bd2116..c143cd72d 100644 --- a/server/block/break_info.go +++ b/server/block/break_info.go @@ -85,7 +85,8 @@ type BreakInfo struct { Drops func(t item.Tool, enchantments []item.Enchantment) []item.Stack // XPDrops is the range of XP a block can drop when broken. XPDrops XPDropRange - // BlastResistance ... + // BlastResistance is the blast resistance of the block, which influences the block's ability to withstand an + // explosive blast. BlastResistance float64 } diff --git a/server/block/explosion.go b/server/block/explosion.go index 461db15a6..7f829105e 100644 --- a/server/block/explosion.go +++ b/server/block/explosion.go @@ -5,6 +5,8 @@ import ( "github.com/df-mc/dragonfly/server/block/cube/trace" "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" "math" "math/rand" @@ -35,21 +37,36 @@ type Explodable interface { } type ExplosionConfig struct { + // World is the world that the explosion will take place. World *world.World - Pos mgl64.Vec3 - Size float64 - Rand rand.Source - Fire bool + // Pos is the position in the world that the explosion will take place at. + Pos mgl64.Vec3 + // Size ... + Size float64 + + // SpawnFire will cause the explosion to randomly start fires in 1/3 of all destroyed air blocks that are + // above opaque blocks. + SpawnFire bool Sound world.Sound Particle world.Particle + + RandSource rand.Source } +// Do ... func (c ExplosionConfig) Do() { - if c.Rand == nil { - c.Rand = rand.NewSource(time.Now().UnixNano()) + if c.Sound == nil { + c.Sound = sound.Explosion{} + } + if c.Particle == nil { + c.Particle = particle.HugeExplosion{} + } + if c.RandSource == nil { + c.RandSource = rand.NewSource(time.Now().UnixNano()) } - r := rand.New(c.Rand) + r := rand.New(c.RandSource) + d := c.Size * 2 bb := cube.Box( math.Floor(c.Pos[0]-d-1), @@ -74,7 +91,7 @@ func (c ExplosionConfig) Do() { } } - var affectedBlocks = make([]cube.Pos, 0, 32) + affectedBlocks := make([]cube.Pos, 0, 32) for _, ray := range rays { pos := c.Pos for blastForce := c.Size * (0.7 + r.Float64()*0.6); blastForce > 0.0; blastForce -= 0.225 { @@ -93,29 +110,25 @@ func (c ExplosionConfig) Do() { explodable.Explode(pos, c) } else if breakable, ok := bl.(Breakable); ok { c.World.SetBlock(pos, nil, nil) - if 1/c.Size > rand.Float64() { + if 1/c.Size > r.Float64() { for _, drop := range breakable.BreakInfo().Drops(item.ToolNone{}, nil) { dropItem(c.World, drop, pos.Vec3Centre()) } } } } - if c.Fire { + if c.SpawnFire { for _, pos := range affectedBlocks { - if rand.Intn(3) == 0 { - if _, ok := c.World.Block(pos).(Air); ok { + if r.Intn(3) == 0 { + if _, ok := c.World.Block(pos).(Air); ok && !c.World.Block(pos.Side(cube.FaceDown)).Model().FaceSolid(pos, cube.FaceUp, c.World) { c.World.SetBlock(pos, Fire{}, nil) } } } } - if c.Particle != nil { - c.World.AddParticle(c.Pos, c.Particle) - } - if c.Sound != nil { - c.World.PlaySound(c.Pos, c.Sound) - } + c.World.AddParticle(c.Pos, c.Particle) + c.World.PlaySound(c.Pos, c.Sound) } // TODO @@ -125,38 +138,36 @@ func exposure(origin mgl64.Vec3, e world.Entity) float64 { bb := e.BBox().Translate(pos) min, max := bb.Min(), bb.Max() diff := max.Sub(min).Mul(2.0).Add(mgl64.Vec3{1, 1, 1}) - double4 := 1.0 / diff[0] - double5 := 1.0 / diff[1] - double6 := 1.0 / diff[2] - double7 := (1.0 - math.Floor(1.0/double4)*double4) / 2.0 - double8 := (1.0 - math.Floor(1.0/double6)*double6) / 2.0 - if double4 < 0.0 || double5 < 0.0 || double6 < 0.0 { + step := mgl64.Vec3{1.0 / diff[0], 1.0 / diff[1], 1.0 / diff[2]} + if step[0] < 0.0 || step[1] < 0.0 || step[2] < 0.0 { return 0.0 } - integer14 := 0.0 - integer15 := 0.0 - for float16 := 0.0; float16 <= 1.0; float16 += double4 { - for float17 := 0.0; float17 <= 1.0; float17 += double5 { - for float18 := 0.0; float18 <= 1.0; float18 += double6 { + double7 := (1.0 - math.Floor(diff[0])/diff[0]) / 2.0 + double8 := (1.0 - math.Floor(diff[2])/diff[2]) / 2.0 + collisions := 0.0 + checks := 0.0 + for x := 0.0; x <= 1.0; x += step[0] { + for y := 0.0; y <= 1.0; y += step[1] { + for z := 0.0; z <= 1.0; z += step[2] { dck2 := mgl64.Vec3{ - lerp(float16, min[0], max[0]) + double7, - lerp(float17, min[1], max[1]), - lerp(float18, min[2], max[2]) + double8, + lerp(x, min[0], max[0]) + double7, + lerp(y, min[1], max[1]), + lerp(z, min[2], max[2]) + double8, } var collides bool - trace.TraverseBlocks(dck2, origin, func(pos cube.Pos) (con bool) { + trace.TraverseBlocks(origin, dck2, func(pos cube.Pos) (con bool) { _, air := w.Block(pos).(Air) collides = !air return air }) if collides { - integer14++ + collisions++ } - integer15++ + checks++ } } } - return integer14 / integer15 + return collisions / checks } func lerp(v, v1, t float64) float64 { From fd081cf9f18a2850eacb2c47f4dada4012e96348 Mon Sep 17 00:00:00 2001 From: JustTal Date: Fri, 15 Jul 2022 15:18:17 -0500 Subject: [PATCH 05/41] block: Added blast resistances for each block. --- server/block/ancient_debris.go | 2 +- server/block/anvil.go | 2 +- server/block/basalt.go | 2 +- server/block/bedrock.go | 5 -- server/block/block.go | 37 +-------- server/block/break_info.go | 15 ++-- server/block/bricks.go | 2 +- server/block/chain.go | 2 +- server/block/coal.go | 2 +- server/block/coal_ore.go | 6 +- server/block/cobblestone.go | 2 +- server/block/cobblestone_stairs.go | 2 +- server/block/cocoa_bean.go | 2 +- server/block/copper_ore.go | 2 +- server/block/coral_block.go | 2 +- server/block/diamond.go | 2 +- server/block/diamond_ore.go | 6 +- server/block/dirt.go | 2 +- server/block/dripstone.go | 2 +- server/block/emerald.go | 2 +- server/block/emerald_ore.go | 6 +- server/block/end_brick_stairs.go | 2 +- server/block/end_stone.go | 2 +- server/block/ender_chest.go | 2 +- server/block/explosion.go | 107 ++++++++++++++++++--------- server/block/gilded_blackstone.go | 2 +- server/block/gold.go | 2 +- server/block/gold_ore.go | 6 +- server/block/grass.go | 2 +- server/block/iron.go | 2 +- server/block/iron_bars.go | 2 +- server/block/iron_ore.go | 6 +- server/block/lapis_ore.go | 6 +- server/block/nether_brick_fence.go | 2 +- server/block/nether_bricks.go | 2 +- server/block/netherite.go | 2 +- server/block/obsidian.go | 2 +- server/block/ore_type.go | 2 +- server/block/packed_mud.go | 2 +- server/block/planks.go | 2 +- server/block/prismarine.go | 3 +- server/block/purpur.go | 2 +- server/block/raw_copper.go | 2 +- server/block/raw_gold.go | 2 +- server/block/raw_iron.go | 2 +- server/block/reinforced_deepslate.go | 2 +- server/block/sandstone_stairs.go | 6 +- server/block/stained_terracotta.go | 2 +- server/block/stone.go | 10 +-- server/block/stone_brick_stairs.go | 2 +- server/block/stone_bricks.go | 2 +- server/block/terracotta.go | 2 +- server/block/tuff.go | 2 +- server/block/wood_fence.go | 2 +- server/block/wood_fence_gate.go | 2 +- server/block/wood_slab.go | 2 +- server/block/wood_stairs.go | 2 +- server/item/inventory/inventory.go | 6 +- 58 files changed, 169 insertions(+), 144 deletions(-) diff --git a/server/block/ancient_debris.go b/server/block/ancient_debris.go index 32f6b2d8e..13cb7ae99 100644 --- a/server/block/ancient_debris.go +++ b/server/block/ancient_debris.go @@ -13,7 +13,7 @@ type AncientDebris struct { func (a AncientDebris) BreakInfo() BreakInfo { return newBreakInfo(30, func(t item.Tool) bool { return t.ToolType() == item.TypePickaxe && t.HarvestLevel() >= item.ToolTierDiamond.HarvestLevel - }, pickaxeEffective, oneOf(a)) + }, pickaxeEffective, oneOf(a)).withBlastResistance(3600) } // EncodeItem ... diff --git a/server/block/anvil.go b/server/block/anvil.go index bb17b45a2..ae6e8511b 100644 --- a/server/block/anvil.go +++ b/server/block/anvil.go @@ -27,7 +27,7 @@ func (a Anvil) Model() world.BlockModel { // BreakInfo ... func (a Anvil) BreakInfo() BreakInfo { - return newBreakInfo(5, pickaxeHarvestable, pickaxeEffective, oneOf(a)) + return newBreakInfo(5, pickaxeHarvestable, pickaxeEffective, oneOf(a)).withBlastResistance(6000) } // Activate ... diff --git a/server/block/basalt.go b/server/block/basalt.go index fd37b0807..47e7db466 100644 --- a/server/block/basalt.go +++ b/server/block/basalt.go @@ -32,7 +32,7 @@ func (b Basalt) UseOnBlock(pos cube.Pos, face cube.Face, _ mgl64.Vec3, w *world. // BreakInfo ... func (b Basalt) BreakInfo() BreakInfo { - return newBreakInfo(1.25, pickaxeHarvestable, pickaxeEffective, oneOf(b)) + return newBreakInfo(1.25, pickaxeHarvestable, pickaxeEffective, oneOf(b)).withBlastResistance(21) } // EncodeItem ... diff --git a/server/block/bedrock.go b/server/block/bedrock.go index a82a5ca63..bb10292c9 100644 --- a/server/block/bedrock.go +++ b/server/block/bedrock.go @@ -11,11 +11,6 @@ type Bedrock struct { InfiniteBurning bool } -// BreakInfo ... -func (b Bedrock) BreakInfo() BreakInfo { - return newBreakInfo(-1, neverHarvestable, nothingEffective, simpleDrops()).withBlastResistance(0.6) -} - // EncodeItem ... func (Bedrock) EncodeItem() (name string, meta int16) { return "minecraft:bedrock", 0 diff --git a/server/block/block.go b/server/block/block.go index 71a01d883..0899a2a4d 100644 --- a/server/block/block.go +++ b/server/block/block.go @@ -3,8 +3,6 @@ package block import ( "github.com/df-mc/dragonfly/server/block/cube" "github.com/df-mc/dragonfly/server/entity/damage" - "github.com/df-mc/dragonfly/server/entity/effect" - "github.com/df-mc/dragonfly/server/entity/healing" "github.com/df-mc/dragonfly/server/item" "github.com/df-mc/dragonfly/server/world" "github.com/df-mc/dragonfly/server/world/sound" @@ -245,17 +243,9 @@ func newFlammabilityInfo(encouragement, flammability int, lavaFlammable bool) Fl } } +// livingEntity ... type livingEntity interface { world.Entity - // Health returns the health of the entity. - Health() float64 - // MaxHealth returns the maximum health of the entity. - MaxHealth() float64 - // SetMaxHealth changes the maximum health of the entity to the value passed. - SetMaxHealth(v float64) - // Dead checks if the entity is considered dead. True is returned if the health of the entity is equal to or - // lower than 0. - Dead() bool // AttackImmune checks if the entity is currently immune to entity attacks. Entities typically turn // immune for half a second after being attacked. AttackImmune() bool @@ -265,33 +255,10 @@ type livingEntity interface { // Hurt returns the final amount of damage dealt to the Living entity and returns whether the Living entity // was vulnerable to the damage at all. Hurt(damage float64, src damage.Source) (n float64, vulnerable bool) - // Heal heals the entity for a given amount of health. The source passed represents the cause of the - // healing, for example healing.SourceFood if the entity healed by having a full food bar. If the health - // added to the original health exceeds the entity's max health, Heal may not add the full amount. - Heal(health float64, src healing.Source) - // KnockBack knocks the entity back with a given force and height. A source is passed which indicates the - // source of the velocity, typically the position of an attacking entity. The source is used to calculate - // the direction which the entity should be knocked back in. - KnockBack(src mgl64.Vec3, force, height float64) - // AddEffect adds an entity.Effect to the entity. If the effect is instant, it is applied to the entity - // immediately. If not, the effect is applied to the entity every time the Tick method is called. - // AddEffect will overwrite any effects present if the level of the effect is higher than the existing one, or - // if the effects' levels are equal and the new effect has a longer duration. - AddEffect(e effect.Effect) - // RemoveEffect removes any effect that might currently be active on the entity. - RemoveEffect(e effect.Type) - // Effects returns any effect currently applied to the entity. The returned effects are guaranteed not to have - // expired when returned. - Effects() []effect.Effect - // Speed returns the current speed of the living entity. The default value is different for each entity. - Speed() float64 - // SetSpeed sets the speed of an entity to a new value. - SetSpeed(float64) } +// flammableEntity ... type flammableEntity interface { - // FireProof is whether the entity is currently fireproof. - FireProof() bool // OnFireDuration returns duration of fire in ticks. OnFireDuration() time.Duration // SetOnFire sets the entity on fire for the specified duration. diff --git a/server/block/break_info.go b/server/block/break_info.go index e81bd2116..47cb8f81c 100644 --- a/server/block/break_info.go +++ b/server/block/break_info.go @@ -85,17 +85,20 @@ type BreakInfo struct { Drops func(t item.Tool, enchantments []item.Enchantment) []item.Stack // XPDrops is the range of XP a block can drop when broken. XPDrops XPDropRange - // BlastResistance ... + // BlastResistance is the blast resistance of the block, which influences the block's ability to withstand an + // explosive blast. BlastResistance float64 } -// newBreakInfo creates a BreakInfo struct with the properties passed. The XPDrops field is 0 by default. +// newBreakInfo creates a BreakInfo struct with the properties passed. The XPDrops field is 0 by default. The blast +// resistance gets set to the hardness by default, as it usually is the same. func newBreakInfo(hardness float64, harvestable func(item.Tool) bool, effective func(item.Tool) bool, drops func(item.Tool, []item.Enchantment) []item.Stack) BreakInfo { return BreakInfo{ - Hardness: hardness, - Harvestable: harvestable, - Effective: effective, - Drops: drops, + Hardness: hardness, + BlastResistance: hardness * 5, // Blast resistance is usually always just hardness multiplied by 5. + Harvestable: harvestable, + Effective: effective, + Drops: drops, } } diff --git a/server/block/bricks.go b/server/block/bricks.go index da87c0eda..3266513d7 100644 --- a/server/block/bricks.go +++ b/server/block/bricks.go @@ -8,7 +8,7 @@ type Bricks struct { // BreakInfo ... func (b Bricks) BreakInfo() BreakInfo { - return newBreakInfo(2, pickaxeHarvestable, pickaxeEffective, oneOf(b)) + return newBreakInfo(2, pickaxeHarvestable, pickaxeEffective, oneOf(b)).withBlastResistance(30) } // EncodeItem ... diff --git a/server/block/chain.go b/server/block/chain.go index 749786526..d196460ec 100644 --- a/server/block/chain.go +++ b/server/block/chain.go @@ -41,7 +41,7 @@ func (c Chain) UseOnBlock(pos cube.Pos, face cube.Face, _ mgl64.Vec3, w *world.W // BreakInfo ... func (c Chain) BreakInfo() BreakInfo { - return newBreakInfo(5, pickaxeHarvestable, pickaxeEffective, oneOf(c)) + return newBreakInfo(5, pickaxeHarvestable, pickaxeEffective, oneOf(c)).withBlastResistance(15) } // EncodeItem ... diff --git a/server/block/coal.go b/server/block/coal.go index b1b135f85..12ac753d7 100644 --- a/server/block/coal.go +++ b/server/block/coal.go @@ -13,7 +13,7 @@ func (c Coal) FlammabilityInfo() FlammabilityInfo { // BreakInfo ... func (c Coal) BreakInfo() BreakInfo { - return newBreakInfo(5, pickaxeHarvestable, pickaxeEffective, oneOf(c)) + return newBreakInfo(5, pickaxeHarvestable, pickaxeEffective, oneOf(c)).withBlastResistance(30) } // EncodeItem ... diff --git a/server/block/coal_ore.go b/server/block/coal_ore.go index 83c28c285..930435745 100644 --- a/server/block/coal_ore.go +++ b/server/block/coal_ore.go @@ -13,7 +13,11 @@ type CoalOre struct { // BreakInfo ... func (c CoalOre) BreakInfo() BreakInfo { - return newBreakInfo(c.Type.Hardness(), pickaxeHarvestable, pickaxeEffective, silkTouchOneOf(item.Coal{}, c)).withXPDropRange(0, 2) + i := newBreakInfo(c.Type.Hardness(), pickaxeHarvestable, pickaxeEffective, silkTouchOneOf(item.Coal{}, c)).withXPDropRange(0, 2) + if c.Type == DeepslateOre() { + i = i.withBlastResistance(9) + } + return i } // EncodeItem ... diff --git a/server/block/cobblestone.go b/server/block/cobblestone.go index e1d14bd2c..4aec07d6d 100644 --- a/server/block/cobblestone.go +++ b/server/block/cobblestone.go @@ -12,7 +12,7 @@ type Cobblestone struct { // BreakInfo ... func (c Cobblestone) BreakInfo() BreakInfo { - return newBreakInfo(2, pickaxeHarvestable, pickaxeEffective, oneOf(c)) + return newBreakInfo(2, pickaxeHarvestable, pickaxeEffective, oneOf(c)).withBlastResistance(30) } // RepairsStoneTools ... diff --git a/server/block/cobblestone_stairs.go b/server/block/cobblestone_stairs.go index b49fb8a09..497655498 100644 --- a/server/block/cobblestone_stairs.go +++ b/server/block/cobblestone_stairs.go @@ -45,7 +45,7 @@ func (s CobblestoneStairs) Model() world.BlockModel { // BreakInfo ... func (s CobblestoneStairs) BreakInfo() BreakInfo { - return newBreakInfo(2, pickaxeHarvestable, pickaxeEffective, oneOf(s)) + return newBreakInfo(2, pickaxeHarvestable, pickaxeEffective, oneOf(s)).withBlastResistance(30) } // EncodeItem ... diff --git a/server/block/cocoa_bean.go b/server/block/cocoa_bean.go index f56f312f4..8ca3d4296 100644 --- a/server/block/cocoa_bean.go +++ b/server/block/cocoa_bean.go @@ -92,7 +92,7 @@ func (c CocoaBean) BreakInfo() BreakInfo { return []item.Stack{item.NewStack(c, rand.Intn(2)+2)} } return []item.Stack{item.NewStack(c, 1)} - }) + }).withBlastResistance(15) } // EncodeItem ... diff --git a/server/block/copper_ore.go b/server/block/copper_ore.go index 802bf78c0..c7e4c8e44 100644 --- a/server/block/copper_ore.go +++ b/server/block/copper_ore.go @@ -18,7 +18,7 @@ type CopperOre struct { func (c CopperOre) BreakInfo() BreakInfo { return newBreakInfo(c.Type.Hardness(), func(t item.Tool) bool { return t.ToolType() == item.TypePickaxe && t.HarvestLevel() >= item.ToolTierStone.HarvestLevel - }, pickaxeEffective, silkTouchDrop(item.NewStack(item.RawCopper{}, rand.Intn(4)+2), item.NewStack(c, 1))) + }, pickaxeEffective, silkTouchDrop(item.NewStack(item.RawCopper{}, rand.Intn(4)+2), item.NewStack(c, 1))).withBlastResistance(9) } // EncodeItem ... diff --git a/server/block/coral_block.go b/server/block/coral_block.go index c15f5fde4..f351b0c32 100644 --- a/server/block/coral_block.go +++ b/server/block/coral_block.go @@ -48,7 +48,7 @@ func (c CoralBlock) ScheduledTick(pos cube.Pos, w *world.World, _ *rand.Rand) { // BreakInfo ... func (c CoralBlock) BreakInfo() BreakInfo { - return newBreakInfo(7, pickaxeHarvestable, pickaxeEffective, silkTouchOneOf(CoralBlock{Type: c.Type, Dead: true}, c)) + return newBreakInfo(7, pickaxeHarvestable, pickaxeEffective, silkTouchOneOf(CoralBlock{Type: c.Type, Dead: true}, c)).withBlastResistance(4.5) } // EncodeBlock ... diff --git a/server/block/diamond.go b/server/block/diamond.go index b46d84e01..cd89c09f9 100644 --- a/server/block/diamond.go +++ b/server/block/diamond.go @@ -13,7 +13,7 @@ type Diamond struct { func (d Diamond) BreakInfo() BreakInfo { return newBreakInfo(5, func(t item.Tool) bool { return t.ToolType() == item.TypePickaxe && t.HarvestLevel() >= item.ToolTierIron.HarvestLevel - }, pickaxeEffective, oneOf(d)) + }, pickaxeEffective, oneOf(d)).withBlastResistance(30) } // PowersBeacon ... diff --git a/server/block/diamond_ore.go b/server/block/diamond_ore.go index ac8985f25..d698fb99b 100644 --- a/server/block/diamond_ore.go +++ b/server/block/diamond_ore.go @@ -15,9 +15,13 @@ type DiamondOre struct { // BreakInfo ... func (d DiamondOre) BreakInfo() BreakInfo { - return newBreakInfo(d.Type.Hardness(), func(t item.Tool) bool { + i := newBreakInfo(d.Type.Hardness(), func(t item.Tool) bool { return t.ToolType() == item.TypePickaxe && t.HarvestLevel() >= item.ToolTierIron.HarvestLevel }, pickaxeEffective, silkTouchOneOf(item.Diamond{}, d)).withXPDropRange(3, 7) + if d.Type == DeepslateOre() { + i = i.withBlastResistance(9) + } + return i } // EncodeItem ... diff --git a/server/block/dirt.go b/server/block/dirt.go index ab1d0cf25..4e3a1cf29 100644 --- a/server/block/dirt.go +++ b/server/block/dirt.go @@ -25,7 +25,7 @@ func (d Dirt) SoilFor(block world.Block) bool { // BreakInfo ... func (d Dirt) BreakInfo() BreakInfo { - return newBreakInfo(0.5, alwaysHarvestable, shovelEffective, oneOf(d)).withBlastResistance(0.5) + return newBreakInfo(0.5, alwaysHarvestable, shovelEffective, oneOf(d)) } // Till ... diff --git a/server/block/dripstone.go b/server/block/dripstone.go index 673721f97..b3bbcb2e7 100644 --- a/server/block/dripstone.go +++ b/server/block/dripstone.go @@ -8,7 +8,7 @@ type Dripstone struct { // BreakInfo ... func (d Dripstone) BreakInfo() BreakInfo { - return newBreakInfo(1.5, pickaxeHarvestable, pickaxeEffective, oneOf(d)) + return newBreakInfo(1.5, pickaxeHarvestable, pickaxeEffective, oneOf(d)).withBlastResistance(5) } // EncodeItem ... diff --git a/server/block/emerald.go b/server/block/emerald.go index 0f39697e8..bebdcb9db 100644 --- a/server/block/emerald.go +++ b/server/block/emerald.go @@ -19,7 +19,7 @@ func (e Emerald) Instrument() sound.Instrument { func (e Emerald) BreakInfo() BreakInfo { return newBreakInfo(5, func(t item.Tool) bool { return t.ToolType() == item.TypePickaxe && t.HarvestLevel() >= item.ToolTierIron.HarvestLevel - }, pickaxeEffective, oneOf(e)) + }, pickaxeEffective, oneOf(e)).withBlastResistance(30) } // PowersBeacon ... diff --git a/server/block/emerald_ore.go b/server/block/emerald_ore.go index ace930363..b43e91fca 100644 --- a/server/block/emerald_ore.go +++ b/server/block/emerald_ore.go @@ -15,9 +15,13 @@ type EmeraldOre struct { // BreakInfo ... func (e EmeraldOre) BreakInfo() BreakInfo { - return newBreakInfo(e.Type.Hardness(), func(t item.Tool) bool { + i := newBreakInfo(e.Type.Hardness(), func(t item.Tool) bool { return t.ToolType() == item.TypePickaxe && t.HarvestLevel() >= item.ToolTierIron.HarvestLevel }, pickaxeEffective, silkTouchOneOf(item.Emerald{}, e)).withXPDropRange(3, 7) + if e.Type == DeepslateOre() { + i = i.withBlastResistance(15) + } + return i } // EncodeItem ... diff --git a/server/block/end_brick_stairs.go b/server/block/end_brick_stairs.go index a0aea5070..45478c35d 100644 --- a/server/block/end_brick_stairs.go +++ b/server/block/end_brick_stairs.go @@ -42,7 +42,7 @@ func (s EndBrickStairs) Model() world.BlockModel { // BreakInfo ... func (s EndBrickStairs) BreakInfo() BreakInfo { - return newBreakInfo(3, pickaxeHarvestable, pickaxeEffective, oneOf(s)) + return newBreakInfo(3, pickaxeHarvestable, pickaxeEffective, oneOf(s)).withBlastResistance(30) } // EncodeItem ... diff --git a/server/block/end_stone.go b/server/block/end_stone.go index 254f0b454..98224ed1c 100644 --- a/server/block/end_stone.go +++ b/server/block/end_stone.go @@ -8,7 +8,7 @@ type EndStone struct { // BreakInfo ... func (e EndStone) BreakInfo() BreakInfo { - return newBreakInfo(3, pickaxeHarvestable, pickaxeEffective, oneOf(e)) + return newBreakInfo(3, pickaxeHarvestable, pickaxeEffective, oneOf(e)).withBlastResistance(45) } // EncodeItem ... diff --git a/server/block/ender_chest.go b/server/block/ender_chest.go index c6524a3b5..4b31f07d1 100644 --- a/server/block/ender_chest.go +++ b/server/block/ender_chest.go @@ -36,7 +36,7 @@ func NewEnderChest() EnderChest { // BreakInfo ... func (c EnderChest) BreakInfo() BreakInfo { - return newBreakInfo(22.5, pickaxeHarvestable, pickaxeEffective, silkTouchDrop(item.NewStack(Obsidian{}, 8), item.NewStack(NewEnderChest(), 1))) + return newBreakInfo(22.5, pickaxeHarvestable, pickaxeEffective, silkTouchDrop(item.NewStack(Obsidian{}, 8), item.NewStack(NewEnderChest(), 1))).withBlastResistance(3000) } // LightEmissionLevel ... diff --git a/server/block/explosion.go b/server/block/explosion.go index 461db15a6..b959ae3fc 100644 --- a/server/block/explosion.go +++ b/server/block/explosion.go @@ -5,14 +5,54 @@ import ( "github.com/df-mc/dragonfly/server/block/cube/trace" "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" "math" "math/rand" "time" ) +// ExplosionConfig is the configuration for an explosion. The world, position, size, sound, particle, and more can all +// be configured through this configuration. +type ExplosionConfig struct { + // World is the world in which the explosion should take place. + World *world.World + // Pos is the center position of the explosion. + Pos mgl64.Vec3 + // Radius is the radius of the explosion. + Radius float64 + // Rand is the source to use for the explosion "randomness". If set to nil, this will default to the global rand. + Rand rand.Source + // CreateFire is true if the explosion should create fire, for example, if it came from a fireball of a ghast or + // a bed in the nether. + CreateFire bool + + // Sound is the sound to play when the explosion is created. If set to nil, this will default to the sound of a + // regular explosion. + Sound world.Sound + // Particle is the particle to spawn when the explosion is created. If set to nil, this will default to the particle + // of a regular huge explosion. + Particle world.Particle +} + +// ExplodableEntity represents an entity that can be exploded. +type ExplodableEntity interface { + // Explode is called when an explosion occurs. The entity can then react to the explosion using the configuration + // and impact provided. + Explode(c ExplosionConfig, impact float64) +} + +// Explodable represents a block that can be exploded. +type Explodable interface { + // Explode is called when an explosion occurs. The block can react to the explosion using the configuration passed. + Explode(pos cube.Pos, c ExplosionConfig) +} + +// rays ... var rays = make([]mgl64.Vec3, 0, 1352) +// init ... func init() { for x := 0.0; x < 16; x++ { for y := 0.0; y < 16; y++ { @@ -26,31 +66,19 @@ func init() { } } -type ExplodableEntity interface { - Explode(c ExplosionConfig, impact float64) -} - -type Explodable interface { - Explode(pos cube.Pos, c ExplosionConfig) -} - -type ExplosionConfig struct { - World *world.World - Pos mgl64.Vec3 - Size float64 - Rand rand.Source - Fire bool - - Sound world.Sound - Particle world.Particle -} - +// Do performs the explosion as specified by the configuration. func (c ExplosionConfig) Do() { + if c.Sound == nil { + c.Sound = sound.Explosion{} + } + if c.Particle == nil { + c.Particle = particle.HugeExplosion{} + } if c.Rand == nil { c.Rand = rand.NewSource(time.Now().UnixNano()) } - r := rand.New(c.Rand) - d := c.Size * 2 + + r, d := rand.New(c.Rand), c.Radius*2 bb := cube.Box( math.Floor(c.Pos[0]-d-1), math.Ceil(c.Pos[0]+d+1), @@ -77,7 +105,7 @@ func (c ExplosionConfig) Do() { var affectedBlocks = make([]cube.Pos, 0, 32) for _, ray := range rays { pos := c.Pos - for blastForce := c.Size * (0.7 + r.Float64()*0.6); blastForce > 0.0; blastForce -= 0.225 { + for blastForce := c.Radius * (0.7 + r.Float64()*0.6); blastForce > 0.0; blastForce -= 0.225 { current := cube.PosFromVec3(pos) if r, ok := c.World.Block(current).(Breakable); ok { if blastForce -= (r.BreakInfo().BlastResistance + 0.3) * 0.3; blastForce > 0 { @@ -93,14 +121,14 @@ func (c ExplosionConfig) Do() { explodable.Explode(pos, c) } else if breakable, ok := bl.(Breakable); ok { c.World.SetBlock(pos, nil, nil) - if 1/c.Size > rand.Float64() { + if 1/c.Radius > rand.Float64() { for _, drop := range breakable.BreakInfo().Drops(item.ToolNone{}, nil) { dropItem(c.World, drop, pos.Vec3Centre()) } } } } - if c.Fire { + if c.CreateFire { for _, pos := range affectedBlocks { if rand.Intn(3) == 0 { if _, ok := c.World.Block(pos).(Air); ok { @@ -118,31 +146,35 @@ func (c ExplosionConfig) Do() { } } -// TODO +// exposure returns the exposure of a block to an entity, used to calculate the impact of an explosion. func exposure(origin mgl64.Vec3, e world.Entity) float64 { w := e.World() pos := e.Position() - bb := e.BBox().Translate(pos) - min, max := bb.Min(), bb.Max() - diff := max.Sub(min).Mul(2.0).Add(mgl64.Vec3{1, 1, 1}) + box := e.BBox().Translate(pos) + + boxMin, boxMax := box.Min(), box.Max() + diff := boxMax.Sub(boxMin).Mul(2.0).Add(mgl64.Vec3{1, 1, 1}) + double4 := 1.0 / diff[0] double5 := 1.0 / diff[1] double6 := 1.0 / diff[2] - double7 := (1.0 - math.Floor(1.0/double4)*double4) / 2.0 - double8 := (1.0 - math.Floor(1.0/double6)*double6) / 2.0 if double4 < 0.0 || double5 < 0.0 || double6 < 0.0 { return 0.0 } - integer14 := 0.0 - integer15 := 0.0 + + double7 := (1.0 - math.Floor(1.0/double4)*double4) / 2.0 + double8 := (1.0 - math.Floor(1.0/double6)*double6) / 2.0 + + var integer14, integer15 float64 for float16 := 0.0; float16 <= 1.0; float16 += double4 { for float17 := 0.0; float17 <= 1.0; float17 += double5 { for float18 := 0.0; float18 <= 1.0; float18 += double6 { dck2 := mgl64.Vec3{ - lerp(float16, min[0], max[0]) + double7, - lerp(float17, min[1], max[1]), - lerp(float18, min[2], max[2]) + double8, + lerp(float16, boxMin[0], boxMax[0]) + double7, + lerp(float17, boxMin[1], boxMax[1]), + lerp(float18, boxMin[2], boxMax[2]) + double8, } + var collides bool trace.TraverseBlocks(dck2, origin, func(pos cube.Pos) (con bool) { _, air := w.Block(pos).(Air) @@ -159,6 +191,7 @@ func exposure(origin mgl64.Vec3, e world.Entity) float64 { return integer14 / integer15 } -func lerp(v, v1, t float64) float64 { - return (1-t)*v + t*v1 +// lerp returns the linear interpolation between a and b at t. +func lerp(a, b, t float64) float64 { + return (1-t)*a + t*b } diff --git a/server/block/gilded_blackstone.go b/server/block/gilded_blackstone.go index 2611577e2..bcc5d4102 100644 --- a/server/block/gilded_blackstone.go +++ b/server/block/gilded_blackstone.go @@ -17,7 +17,7 @@ func (b GildedBlackstone) BreakInfo() BreakInfo { return []item.Stack{item.NewStack(item.GoldNugget{}, rand.Intn(4)+2)} } return []item.Stack{item.NewStack(b, 1)} - }) + }).withBlastResistance(30) } // EncodeItem ... diff --git a/server/block/gold.go b/server/block/gold.go index 85a676d7a..6e901e76a 100644 --- a/server/block/gold.go +++ b/server/block/gold.go @@ -19,7 +19,7 @@ func (g Gold) Instrument() sound.Instrument { func (g Gold) BreakInfo() BreakInfo { return newBreakInfo(5, func(t item.Tool) bool { return t.ToolType() == item.TypePickaxe && t.HarvestLevel() >= item.ToolTierIron.HarvestLevel - }, pickaxeEffective, oneOf(g)) + }, pickaxeEffective, oneOf(g)).withBlastResistance(30) } // PowersBeacon ... diff --git a/server/block/gold_ore.go b/server/block/gold_ore.go index 7bfc72589..03a58e99e 100644 --- a/server/block/gold_ore.go +++ b/server/block/gold_ore.go @@ -15,9 +15,13 @@ type GoldOre struct { // BreakInfo ... func (g GoldOre) BreakInfo() BreakInfo { - return newBreakInfo(g.Type.Hardness(), func(t item.Tool) bool { + i := newBreakInfo(g.Type.Hardness(), func(t item.Tool) bool { return t.ToolType() == item.TypePickaxe && t.HarvestLevel() >= item.ToolTierIron.HarvestLevel }, pickaxeEffective, silkTouchOneOf(item.RawGold{}, g)) + if g.Type == DeepslateOre() { + i = i.withBlastResistance(9) + } + return i } // EncodeItem ... diff --git a/server/block/grass.go b/server/block/grass.go index c2289ab35..54bc5a839 100644 --- a/server/block/grass.go +++ b/server/block/grass.go @@ -92,7 +92,7 @@ func (g Grass) BoneMeal(pos cube.Pos, w *world.World) bool { // BreakInfo ... func (g Grass) BreakInfo() BreakInfo { - return newBreakInfo(0.6, alwaysHarvestable, shovelEffective, silkTouchOneOf(Dirt{}, g)).withBlastResistance(0.6) + return newBreakInfo(0.6, alwaysHarvestable, shovelEffective, silkTouchOneOf(Dirt{}, g)) } // EncodeItem ... diff --git a/server/block/iron.go b/server/block/iron.go index 79a401340..87903c995 100644 --- a/server/block/iron.go +++ b/server/block/iron.go @@ -19,7 +19,7 @@ func (i Iron) Instrument() sound.Instrument { func (i Iron) BreakInfo() BreakInfo { return newBreakInfo(5, func(t item.Tool) bool { return t.ToolType() == item.TypePickaxe && t.HarvestLevel() >= item.ToolTierStone.HarvestLevel - }, pickaxeEffective, oneOf(i)) + }, pickaxeEffective, oneOf(i)).withBlastResistance(30) } // PowersBeacon ... diff --git a/server/block/iron_bars.go b/server/block/iron_bars.go index 869544633..f12056c29 100644 --- a/server/block/iron_bars.go +++ b/server/block/iron_bars.go @@ -13,7 +13,7 @@ type IronBars struct { // BreakInfo ... func (i IronBars) BreakInfo() BreakInfo { - return newBreakInfo(5, pickaxeHarvestable, pickaxeEffective, oneOf(i)) + return newBreakInfo(5, pickaxeHarvestable, pickaxeEffective, oneOf(i)).withBlastResistance(30) } // CanDisplace ... diff --git a/server/block/iron_ore.go b/server/block/iron_ore.go index e8a5556e0..5225a88d5 100644 --- a/server/block/iron_ore.go +++ b/server/block/iron_ore.go @@ -15,9 +15,13 @@ type IronOre struct { // BreakInfo ... func (i IronOre) BreakInfo() BreakInfo { - return newBreakInfo(i.Type.Hardness(), func(t item.Tool) bool { + b := newBreakInfo(i.Type.Hardness(), func(t item.Tool) bool { return t.ToolType() == item.TypePickaxe && t.HarvestLevel() >= item.ToolTierStone.HarvestLevel }, pickaxeEffective, silkTouchOneOf(item.RawIron{}, i)) + if i.Type == DeepslateOre() { + b = b.withBlastResistance(9) + } + return b } // EncodeItem ... diff --git a/server/block/lapis_ore.go b/server/block/lapis_ore.go index c56f7d7e4..5d3909d99 100644 --- a/server/block/lapis_ore.go +++ b/server/block/lapis_ore.go @@ -16,9 +16,13 @@ type LapisOre struct { // BreakInfo ... func (l LapisOre) BreakInfo() BreakInfo { - return newBreakInfo(l.Type.Hardness(), func(t item.Tool) bool { + i := newBreakInfo(l.Type.Hardness(), func(t item.Tool) bool { return t.ToolType() == item.TypePickaxe && t.HarvestLevel() >= item.ToolTierStone.HarvestLevel }, pickaxeEffective, silkTouchDrop(item.NewStack(item.LapisLazuli{}, rand.Intn(5)+4), item.NewStack(l, 1))).withXPDropRange(2, 5) + if l.Type == DeepslateOre() { + i = i.withBlastResistance(9) + } + return i } // EncodeItem ... diff --git a/server/block/nether_brick_fence.go b/server/block/nether_brick_fence.go index f02294afe..c0cc58db7 100644 --- a/server/block/nether_brick_fence.go +++ b/server/block/nether_brick_fence.go @@ -13,7 +13,7 @@ type NetherBrickFence struct { // BreakInfo ... func (n NetherBrickFence) BreakInfo() BreakInfo { - return newBreakInfo(2, pickaxeHarvestable, pickaxeEffective, oneOf(n)) + return newBreakInfo(2, pickaxeHarvestable, pickaxeEffective, oneOf(n)).withBlastResistance(30) } // CanDisplace ... diff --git a/server/block/nether_bricks.go b/server/block/nether_bricks.go index 5af190f21..d41b42d06 100644 --- a/server/block/nether_bricks.go +++ b/server/block/nether_bricks.go @@ -14,7 +14,7 @@ type NetherBricks struct { // BreakInfo ... func (n NetherBricks) BreakInfo() BreakInfo { - return newBreakInfo(2, pickaxeHarvestable, pickaxeEffective, oneOf(n)) + return newBreakInfo(2, pickaxeHarvestable, pickaxeEffective, oneOf(n)).withBlastResistance(30) } // EncodeItem ... diff --git a/server/block/netherite.go b/server/block/netherite.go index 7eb9c2f46..3900ab172 100644 --- a/server/block/netherite.go +++ b/server/block/netherite.go @@ -14,7 +14,7 @@ type Netherite struct { func (n Netherite) BreakInfo() BreakInfo { return newBreakInfo(50, func(t item.Tool) bool { return t.ToolType() == item.TypePickaxe && t.HarvestLevel() >= item.ToolTierDiamond.HarvestLevel - }, pickaxeEffective, oneOf(n)) + }, pickaxeEffective, oneOf(n)).withBlastResistance(3600) } // PowersBeacon ... diff --git a/server/block/obsidian.go b/server/block/obsidian.go index 71f34b121..0a076116e 100644 --- a/server/block/obsidian.go +++ b/server/block/obsidian.go @@ -41,5 +41,5 @@ func (o Obsidian) EncodeBlock() (string, map[string]any) { func (o Obsidian) BreakInfo() BreakInfo { return newBreakInfo(50, func(t item.Tool) bool { return t.ToolType() == item.TypePickaxe && t.HarvestLevel() >= item.ToolTierDiamond.HarvestLevel - }, pickaxeEffective, oneOf(o)) + }, pickaxeEffective, oneOf(o)).withBlastResistance(6000) } diff --git a/server/block/ore_type.go b/server/block/ore_type.go index ba55b4973..cd9b20e41 100644 --- a/server/block/ore_type.go +++ b/server/block/ore_type.go @@ -66,7 +66,7 @@ func (o ore) Hardness() float64 { case 0: return 3 case 1: - return 4.5 + return 3.5 } panic("unknown ore type") } diff --git a/server/block/packed_mud.go b/server/block/packed_mud.go index 174ee21bf..e3f388763 100644 --- a/server/block/packed_mud.go +++ b/server/block/packed_mud.go @@ -7,7 +7,7 @@ type PackedMud struct { // BreakInfo ... func (p PackedMud) BreakInfo() BreakInfo { - return newBreakInfo(1, alwaysHarvestable, nothingEffective, oneOf(p)) + return newBreakInfo(1, alwaysHarvestable, nothingEffective, oneOf(p)).withBlastResistance(15) } // EncodeItem ... diff --git a/server/block/planks.go b/server/block/planks.go index 7d53ec467..07277e725 100644 --- a/server/block/planks.go +++ b/server/block/planks.go @@ -24,7 +24,7 @@ func (p Planks) FlammabilityInfo() FlammabilityInfo { // BreakInfo ... func (p Planks) BreakInfo() BreakInfo { - return newBreakInfo(2, alwaysHarvestable, axeEffective, oneOf(p)) + return newBreakInfo(2, alwaysHarvestable, axeEffective, oneOf(p)).withBlastResistance(15) } // RepairsWoodTools ... diff --git a/server/block/prismarine.go b/server/block/prismarine.go index e1083b1fe..63d2ad079 100644 --- a/server/block/prismarine.go +++ b/server/block/prismarine.go @@ -15,7 +15,7 @@ type Prismarine struct { // BreakInfo ... func (p Prismarine) BreakInfo() BreakInfo { - return newBreakInfo(1.5, pickaxeHarvestable, pickaxeEffective, oneOf(p)) + return newBreakInfo(1.5, pickaxeHarvestable, pickaxeEffective, oneOf(p)).withBlastResistance(30) } // EncodeItem ... @@ -33,6 +33,5 @@ func allPrismarine() (c []world.Block) { for _, t := range PrismarineTypes() { c = append(c, Prismarine{Type: t}) } - return } diff --git a/server/block/purpur.go b/server/block/purpur.go index bf4d9a2c2..3f786dccb 100644 --- a/server/block/purpur.go +++ b/server/block/purpur.go @@ -25,7 +25,7 @@ type ( // BreakInfo ... func (p Purpur) BreakInfo() BreakInfo { - return newBreakInfo(1.5, pickaxeHarvestable, pickaxeEffective, oneOf(p)) + return newBreakInfo(1.5, pickaxeHarvestable, pickaxeEffective, oneOf(p)).withBlastResistance(30) } // EncodeItem ... diff --git a/server/block/raw_copper.go b/server/block/raw_copper.go index 3cf16748f..97fdb1c2d 100644 --- a/server/block/raw_copper.go +++ b/server/block/raw_copper.go @@ -14,7 +14,7 @@ type RawCopper struct { func (r RawCopper) BreakInfo() BreakInfo { return newBreakInfo(5, func(t item.Tool) bool { return t.ToolType() == item.TypePickaxe && t.HarvestLevel() >= item.ToolTierStone.HarvestLevel - }, pickaxeEffective, oneOf(r)) + }, pickaxeEffective, oneOf(r)).withBlastResistance(30) } // EncodeItem ... diff --git a/server/block/raw_gold.go b/server/block/raw_gold.go index 37205a1b9..c47082bbd 100644 --- a/server/block/raw_gold.go +++ b/server/block/raw_gold.go @@ -14,7 +14,7 @@ type RawGold struct { func (g RawGold) BreakInfo() BreakInfo { return newBreakInfo(5, func(t item.Tool) bool { return t.ToolType() == item.TypePickaxe && t.HarvestLevel() >= item.ToolTierIron.HarvestLevel - }, pickaxeEffective, oneOf(g)) + }, pickaxeEffective, oneOf(g)).withBlastResistance(30) } // EncodeItem ... diff --git a/server/block/raw_iron.go b/server/block/raw_iron.go index f420e9293..100919ee8 100644 --- a/server/block/raw_iron.go +++ b/server/block/raw_iron.go @@ -14,7 +14,7 @@ type RawIron struct { func (r RawIron) BreakInfo() BreakInfo { return newBreakInfo(5, func(t item.Tool) bool { return t.ToolType() == item.TypePickaxe && t.HarvestLevel() >= item.ToolTierStone.HarvestLevel - }, pickaxeEffective, oneOf(r)) + }, pickaxeEffective, oneOf(r)).withBlastResistance(30) } // EncodeItem ... diff --git a/server/block/reinforced_deepslate.go b/server/block/reinforced_deepslate.go index 62e13035d..c3c9e0623 100644 --- a/server/block/reinforced_deepslate.go +++ b/server/block/reinforced_deepslate.go @@ -8,7 +8,7 @@ type ReinforcedDeepslate struct { // BreakInfo ... func (r ReinforcedDeepslate) BreakInfo() BreakInfo { - return newBreakInfo(55, alwaysHarvestable, nothingEffective, oneOf(r)) + return newBreakInfo(55, alwaysHarvestable, nothingEffective, oneOf(r)).withBlastResistance(3600) } // EncodeItem ... diff --git a/server/block/sandstone_stairs.go b/server/block/sandstone_stairs.go index ea39de403..9a1dbf2ed 100644 --- a/server/block/sandstone_stairs.go +++ b/server/block/sandstone_stairs.go @@ -49,7 +49,11 @@ func (s SandstoneStairs) Model() world.BlockModel { // BreakInfo ... func (s SandstoneStairs) BreakInfo() BreakInfo { - return newBreakInfo(s.Type.Hardness(), pickaxeHarvestable, pickaxeEffective, oneOf(s)) + i := newBreakInfo(s.Type.Hardness(), pickaxeHarvestable, pickaxeEffective, oneOf(s)) + if s.Type == SmoothSandstone() { + i = i.withBlastResistance(30) + } + return i } // EncodeItem ... diff --git a/server/block/stained_terracotta.go b/server/block/stained_terracotta.go index a16bcc9a1..1e92afceb 100644 --- a/server/block/stained_terracotta.go +++ b/server/block/stained_terracotta.go @@ -23,7 +23,7 @@ func (t StainedTerracotta) SoilFor(block world.Block) bool { // BreakInfo ... func (t StainedTerracotta) BreakInfo() BreakInfo { - return newBreakInfo(1.25, pickaxeHarvestable, pickaxeEffective, oneOf(t)) + return newBreakInfo(1.25, pickaxeHarvestable, pickaxeEffective, oneOf(t)).withBlastResistance(21) } // EncodeItem ... diff --git a/server/block/stone.go b/server/block/stone.go index 3a73bfdde..c148ab2c8 100644 --- a/server/block/stone.go +++ b/server/block/stone.go @@ -30,24 +30,24 @@ type ( // BreakInfo ... func (s Stone) BreakInfo() BreakInfo { if s.Smooth { - return newBreakInfo(2, pickaxeHarvestable, pickaxeEffective, oneOf(s)) + return newBreakInfo(2, pickaxeHarvestable, pickaxeEffective, oneOf(s)).withBlastResistance(30) } - return newBreakInfo(1.5, pickaxeHarvestable, pickaxeEffective, silkTouchOneOf(Cobblestone{}, Stone{})) + return newBreakInfo(1.5, pickaxeHarvestable, pickaxeEffective, silkTouchOneOf(Cobblestone{}, Stone{})).withBlastResistance(30) } // BreakInfo ... func (g Granite) BreakInfo() BreakInfo { - return newBreakInfo(1.5, pickaxeHarvestable, pickaxeEffective, oneOf(g)) + return newBreakInfo(1.5, pickaxeHarvestable, pickaxeEffective, oneOf(g)).withBlastResistance(30) } // BreakInfo ... func (d Diorite) BreakInfo() BreakInfo { - return newBreakInfo(1.5, pickaxeHarvestable, pickaxeEffective, oneOf(d)) + return newBreakInfo(1.5, pickaxeHarvestable, pickaxeEffective, oneOf(d)).withBlastResistance(30) } // BreakInfo ... func (a Andesite) BreakInfo() BreakInfo { - return newBreakInfo(1.5, pickaxeHarvestable, pickaxeEffective, oneOf(a)) + return newBreakInfo(1.5, pickaxeHarvestable, pickaxeEffective, oneOf(a)).withBlastResistance(30) } // EncodeItem ... diff --git a/server/block/stone_brick_stairs.go b/server/block/stone_brick_stairs.go index 82e302ce9..5178c8d1f 100644 --- a/server/block/stone_brick_stairs.go +++ b/server/block/stone_brick_stairs.go @@ -44,7 +44,7 @@ func (s StoneBrickStairs) Model() world.BlockModel { // BreakInfo ... func (s StoneBrickStairs) BreakInfo() BreakInfo { - return newBreakInfo(1.5, pickaxeHarvestable, pickaxeEffective, oneOf(s)) + return newBreakInfo(1.5, pickaxeHarvestable, pickaxeEffective, oneOf(s)).withBlastResistance(30) } // EncodeItem ... diff --git a/server/block/stone_bricks.go b/server/block/stone_bricks.go index f9868408a..79023e05b 100644 --- a/server/block/stone_bricks.go +++ b/server/block/stone_bricks.go @@ -14,7 +14,7 @@ type StoneBricks struct { // BreakInfo ... func (c StoneBricks) BreakInfo() BreakInfo { - return newBreakInfo(1.5, pickaxeHarvestable, pickaxeEffective, oneOf(c)) + return newBreakInfo(1.5, pickaxeHarvestable, pickaxeEffective, oneOf(c)).withBlastResistance(30) } // EncodeItem ... diff --git a/server/block/terracotta.go b/server/block/terracotta.go index 2ba779a9b..0490068a9 100644 --- a/server/block/terracotta.go +++ b/server/block/terracotta.go @@ -17,7 +17,7 @@ func (Terracotta) SoilFor(block world.Block) bool { // BreakInfo ... func (t Terracotta) BreakInfo() BreakInfo { - return newBreakInfo(1.25, pickaxeHarvestable, pickaxeEffective, oneOf(t)) + return newBreakInfo(1.25, pickaxeHarvestable, pickaxeEffective, oneOf(t)).withBlastResistance(21) } // EncodeItem ... diff --git a/server/block/tuff.go b/server/block/tuff.go index 19d1c8c49..24457951a 100644 --- a/server/block/tuff.go +++ b/server/block/tuff.go @@ -8,7 +8,7 @@ type Tuff struct { // BreakInfo ... func (t Tuff) BreakInfo() BreakInfo { - return newBreakInfo(1.5, pickaxeHarvestable, pickaxeEffective, oneOf(t)) + return newBreakInfo(1.5, pickaxeHarvestable, pickaxeEffective, oneOf(t)).withBlastResistance(30) } // EncodeItem ... diff --git a/server/block/wood_fence.go b/server/block/wood_fence.go index 972b503d3..47089275c 100644 --- a/server/block/wood_fence.go +++ b/server/block/wood_fence.go @@ -19,7 +19,7 @@ type WoodFence struct { // BreakInfo ... func (w WoodFence) BreakInfo() BreakInfo { - return newBreakInfo(2, alwaysHarvestable, axeEffective, oneOf(w)) + return newBreakInfo(2, alwaysHarvestable, axeEffective, oneOf(w)).withBlastResistance(15) } // CanDisplace ... diff --git a/server/block/wood_fence_gate.go b/server/block/wood_fence_gate.go index c15d7e594..2c95b3e32 100644 --- a/server/block/wood_fence_gate.go +++ b/server/block/wood_fence_gate.go @@ -27,7 +27,7 @@ type WoodFenceGate struct { // BreakInfo ... func (f WoodFenceGate) BreakInfo() BreakInfo { - return newBreakInfo(2, alwaysHarvestable, axeEffective, oneOf(f)) + return newBreakInfo(2, alwaysHarvestable, axeEffective, oneOf(f)).withBlastResistance(15) } // FlammabilityInfo ... diff --git a/server/block/wood_slab.go b/server/block/wood_slab.go index f7b8ca768..1aebfc563 100644 --- a/server/block/wood_slab.go +++ b/server/block/wood_slab.go @@ -80,7 +80,7 @@ func (s WoodSlab) BreakInfo() BreakInfo { return []item.Stack{item.NewStack(s, 2)} } return []item.Stack{item.NewStack(s, 1)} - }) + }).withBlastResistance(15) } // LightDiffusionLevel returns 0 if the slab is a half slab, or 15 if it is double. diff --git a/server/block/wood_stairs.go b/server/block/wood_stairs.go index 6123b4628..b35216360 100644 --- a/server/block/wood_stairs.go +++ b/server/block/wood_stairs.go @@ -54,7 +54,7 @@ func (s WoodStairs) Model() world.BlockModel { // BreakInfo ... func (s WoodStairs) BreakInfo() BreakInfo { - return newBreakInfo(2, alwaysHarvestable, axeEffective, oneOf(s)) + return newBreakInfo(2, alwaysHarvestable, axeEffective, oneOf(s)).withBlastResistance(15) } // EncodeItem ... diff --git a/server/item/inventory/inventory.go b/server/item/inventory/inventory.go index 67fc9acfd..7373dfee0 100644 --- a/server/item/inventory/inventory.go +++ b/server/item/inventory/inventory.go @@ -25,7 +25,7 @@ type Inventory struct { // ErrSlotOutOfRange is returned by any methods on inventory when a slot is passed which is not within the // range of valid values for the inventory. -var ErrSlotOutOfRange = errors.New("slot is out of range: must be in range 0 <= slot < inventory.Size()") +var ErrSlotOutOfRange = errors.New("slot is out of range: must be in range 0 <= slot < inventory.Radius()") // New creates a new inventory with the size passed. The inventory size cannot be changed after it has been // constructed. @@ -44,7 +44,7 @@ func New(size int, f func(slot int, item item.Stack)) *Inventory { // Item attempts to obtain an item from a specific slot in the inventory. If an item was present in that slot, // the item is returned and the error is nil. If no item was present in the slot, a Stack with air as its item // and a count of 0 is returned. Stack.Empty() may be called to check if this is the case. -// Item only returns an error if the slot passed is out of range. (0 <= slot < inventory.Size()) +// Item only returns an error if the slot passed is out of range. (0 <= slot < inventory.Radius()) func (inv *Inventory) Item(slot int) (item.Stack, error) { inv.check() if !inv.validSlot(slot) { @@ -59,7 +59,7 @@ func (inv *Inventory) Item(slot int) (item.Stack, error) { // SetItem sets a stack of items to a specific slot in the inventory. If an item is already present in the // slot, that item will be overwritten. -// SetItem will return an error if the slot passed is out of range. (0 <= slot < inventory.Size()) +// SetItem will return an error if the slot passed is out of range. (0 <= slot < inventory.Radius()) func (inv *Inventory) SetItem(slot int, item item.Stack) error { inv.check() if !inv.validSlot(slot) { From f0886fbf6853c8c62aac90b1d61fc7684f921b88 Mon Sep 17 00:00:00 2001 From: JustTal Date: Fri, 15 Jul 2022 15:28:06 -0500 Subject: [PATCH 06/41] block/break_info.go: Improve doc. --- server/block/break_info.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/block/break_info.go b/server/block/break_info.go index 47cb8f81c..a83c66901 100644 --- a/server/block/break_info.go +++ b/server/block/break_info.go @@ -91,11 +91,11 @@ type BreakInfo struct { } // newBreakInfo creates a BreakInfo struct with the properties passed. The XPDrops field is 0 by default. The blast -// resistance gets set to the hardness by default, as it usually is the same. +// resistance gets set to the hardness multiplied by five by default, as it usually is the same. func newBreakInfo(hardness float64, harvestable func(item.Tool) bool, effective func(item.Tool) bool, drops func(item.Tool, []item.Enchantment) []item.Stack) BreakInfo { return BreakInfo{ Hardness: hardness, - BlastResistance: hardness * 5, // Blast resistance is usually always just hardness multiplied by 5. + BlastResistance: hardness * 5, Harvestable: harvestable, Effective: effective, Drops: drops, From a134491b3c064fa1398534cb234ff78b616b3d84 Mon Sep 17 00:00:00 2001 From: Tal Date: Fri, 15 Jul 2022 15:32:02 -0500 Subject: [PATCH 07/41] Update server/block/explosion.go Co-authored-by: Raptor --- server/block/explosion.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/block/explosion.go b/server/block/explosion.go index 2c3ddf470..5334c00ef 100644 --- a/server/block/explosion.go +++ b/server/block/explosion.go @@ -142,7 +142,7 @@ func (c ExplosionConfig) Do() { c.World.PlaySound(c.Pos, c.Sound) } -// exposure returns the exposure of a block to an entity, used to calculate the impact of an explosion. +// exposure returns the exposure of an explosion to an entity, used to calculate the impact of an explosion. func exposure(origin mgl64.Vec3, e world.Entity) float64 { w := e.World() pos := e.Position() From 489570fb63a88142cfe55159b127a1238499c630 Mon Sep 17 00:00:00 2001 From: JustTal Date: Fri, 15 Jul 2022 15:36:01 -0500 Subject: [PATCH 08/41] block/explosion.go: Use `box` for consistency. --- server/block/explosion.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/server/block/explosion.go b/server/block/explosion.go index 2c3ddf470..07adee68b 100644 --- a/server/block/explosion.go +++ b/server/block/explosion.go @@ -77,9 +77,9 @@ func (c ExplosionConfig) Do() { if c.RandSource == nil { c.RandSource = rand.NewSource(time.Now().UnixNano()) } - + r, d := rand.New(c.RandSource), c.Radius*2 - bb := cube.Box( + box := cube.Box( math.Floor(c.Pos[0]-d-1), math.Ceil(c.Pos[0]+d+1), math.Floor(c.Pos[1]-d-1), @@ -88,9 +88,9 @@ func (c ExplosionConfig) Do() { math.Ceil(c.Pos[2]+d+1), ) - for _, e := range c.World.EntitiesWithin(bb.Grow(2), nil) { + for _, e := range c.World.EntitiesWithin(box.Grow(2), nil) { pos := e.Position() - if !e.BBox().Translate(pos).IntersectsWith(bb) { + if !e.BBox().Translate(pos).IntersectsWith(box) { continue } dist := pos.Sub(c.Pos).Len() From 97b9cf641bbfdb4a9c48eae20473d7f320ac719e Mon Sep 17 00:00:00 2001 From: T14Raptor Date: Fri, 15 Jul 2022 16:41:16 -0400 Subject: [PATCH 09/41] block/break_info.go: Don't multiply hardness by 5. --- main.go | 6 +++--- server/block/break_info.go | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/main.go b/main.go index 395aa5d98..59fb321e5 100644 --- a/main.go +++ b/main.go @@ -47,9 +47,9 @@ type h struct { func (h h) HandleChat(*event.Context, *string) { block.ExplosionConfig{ - World: h.p.World(), - Pos: h.p.Position(), - Size: 4, + World: h.p.World(), + Pos: h.p.Position(), + Radius: 4, }.Do() } diff --git a/server/block/break_info.go b/server/block/break_info.go index a83c66901..5e27ca9bd 100644 --- a/server/block/break_info.go +++ b/server/block/break_info.go @@ -95,7 +95,7 @@ type BreakInfo struct { func newBreakInfo(hardness float64, harvestable func(item.Tool) bool, effective func(item.Tool) bool, drops func(item.Tool, []item.Enchantment) []item.Stack) BreakInfo { return BreakInfo{ Hardness: hardness, - BlastResistance: hardness * 5, + BlastResistance: hardness, Harvestable: harvestable, Effective: effective, Drops: drops, From 294f6739d1097832c0fcb0016347f26600c0e59f Mon Sep 17 00:00:00 2001 From: T14Raptor Date: Fri, 15 Jul 2022 16:42:26 -0400 Subject: [PATCH 10/41] block/break_info.go: Correct doc. --- server/block/break_info.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/block/break_info.go b/server/block/break_info.go index 5e27ca9bd..0d967f2b9 100644 --- a/server/block/break_info.go +++ b/server/block/break_info.go @@ -91,7 +91,7 @@ type BreakInfo struct { } // newBreakInfo creates a BreakInfo struct with the properties passed. The XPDrops field is 0 by default. The blast -// resistance gets set to the hardness multiplied by five by default, as it usually is the same. +// resistance is set to the hardness by default, as it usually is the same. func newBreakInfo(hardness float64, harvestable func(item.Tool) bool, effective func(item.Tool) bool, drops func(item.Tool, []item.Enchantment) []item.Stack) BreakInfo { return BreakInfo{ Hardness: hardness, From 020bf23a5b6a32c8779645240067e8fd0af31ece Mon Sep 17 00:00:00 2001 From: JustTal Date: Fri, 15 Jul 2022 15:52:54 -0500 Subject: [PATCH 11/41] block/explosion.go: Fix SpawnFire logic. --- server/block/explosion.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/block/explosion.go b/server/block/explosion.go index 5fb933339..95aa8427c 100644 --- a/server/block/explosion.go +++ b/server/block/explosion.go @@ -131,7 +131,7 @@ func (c ExplosionConfig) Do() { if c.SpawnFire { for _, pos := range affectedBlocks { if r.Intn(3) == 0 { - if _, ok := c.World.Block(pos).(Air); ok && !c.World.Block(pos.Side(cube.FaceDown)).Model().FaceSolid(pos, cube.FaceUp, c.World) { + if _, ok := c.World.Block(pos).(Air); ok && c.World.Block(pos.Side(cube.FaceDown)).Model().FaceSolid(pos, cube.FaceUp, c.World) { c.World.SetBlock(pos, Fire{}, nil) } } From c026aa59818335acc9473ff63577bee40eaaa05e Mon Sep 17 00:00:00 2001 From: JustTal Date: Fri, 15 Jul 2022 16:10:53 -0500 Subject: [PATCH 12/41] damage/source.go: Doc SourceExplosion. --- server/entity/damage/source.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/entity/damage/source.go b/server/entity/damage/source.go index 39621b02d..31a0afe65 100644 --- a/server/entity/damage/source.go +++ b/server/entity/damage/source.go @@ -74,7 +74,7 @@ type ( Block world.Block } - // SourceExplosion ... + // SourceExplosion is used for damage caused by an explosion. SourceExplosion struct{} ) From 011b7f4c5091cd33685f44d5fda145bce3e57ea8 Mon Sep 17 00:00:00 2001 From: T14Raptor Date: Fri, 15 Jul 2022 17:18:16 -0400 Subject: [PATCH 13/41] player/player.go: Implement ExplodableEntity. --- server/block/explosion.go | 7 ++++--- server/player/player.go | 9 +++++++++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/server/block/explosion.go b/server/block/explosion.go index 95aa8427c..35e046c6a 100644 --- a/server/block/explosion.go +++ b/server/block/explosion.go @@ -81,10 +81,10 @@ func (c ExplosionConfig) Do() { r, d := rand.New(c.RandSource), c.Radius*2 box := cube.Box( math.Floor(c.Pos[0]-d-1), - math.Ceil(c.Pos[0]+d+1), math.Floor(c.Pos[1]-d-1), - math.Ceil(c.Pos[1]+d+1), math.Floor(c.Pos[2]-d-1), + math.Ceil(c.Pos[0]+d+1), + math.Ceil(c.Pos[1]+d+1), math.Ceil(c.Pos[2]+d+1), ) @@ -98,7 +98,8 @@ func (c ExplosionConfig) Do() { continue } if explodable, ok := e.(ExplodableEntity); ok { - explodable.Explode(c, (1-dist/d)*exposure(c.Pos, e)) + // TODO: exposure + explodable.Explode(c, 1-dist/d) } } diff --git a/server/player/player.go b/server/player/player.go index 1d79447f1..32e7cc64b 100644 --- a/server/player/player.go +++ b/server/player/player.go @@ -619,6 +619,15 @@ func (p *Player) FinalDamageFrom(dmg float64, src damage.Source) float64 { return math.Max(dmg, 0) } +// Explode ... +func (p *Player) Explode(c block.ExplosionConfig, impact float64) { + fmt.Println(impact) + p.Hurt(math.Floor((impact*impact+impact)*3.5*c.Radius+1), damage.SourceExplosion{}) + + v := p.Position().Sub(c.Pos).Normalize() + p.KnockBack(c.Pos, impact, v[1]*impact) +} + // protectionEnchantment represents an enchantment that can protect the player from damage. type protectionEnchantment interface { Affects(damage.Source) bool From 7a9f47a6e598d2260a00b9565c95f1e6b2d67864 Mon Sep 17 00:00:00 2001 From: JustTal Date: Fri, 15 Jul 2022 16:53:43 -0500 Subject: [PATCH 14/41] player/player.go: Directly set velocity. --- server/player/player.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/server/player/player.go b/server/player/player.go index 32e7cc64b..37731e64c 100644 --- a/server/player/player.go +++ b/server/player/player.go @@ -621,11 +621,8 @@ func (p *Player) FinalDamageFrom(dmg float64, src damage.Source) float64 { // Explode ... func (p *Player) Explode(c block.ExplosionConfig, impact float64) { - fmt.Println(impact) p.Hurt(math.Floor((impact*impact+impact)*3.5*c.Radius+1), damage.SourceExplosion{}) - - v := p.Position().Sub(c.Pos).Normalize() - p.KnockBack(c.Pos, impact, v[1]*impact) + p.SetVelocity(p.Position().Sub(c.Pos).Normalize().Mul(impact)) } // protectionEnchantment represents an enchantment that can protect the player from damage. From d6fabcc729198d64d3920afdf27e7bad4556c5c1 Mon Sep 17 00:00:00 2001 From: JustTal Date: Fri, 15 Jul 2022 16:56:01 -0500 Subject: [PATCH 15/41] player/player.go: Revert "Directly set velocity". --- server/player/player.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/server/player/player.go b/server/player/player.go index 37731e64c..3a2918d9e 100644 --- a/server/player/player.go +++ b/server/player/player.go @@ -622,7 +622,9 @@ func (p *Player) FinalDamageFrom(dmg float64, src damage.Source) float64 { // Explode ... func (p *Player) Explode(c block.ExplosionConfig, impact float64) { p.Hurt(math.Floor((impact*impact+impact)*3.5*c.Radius+1), damage.SourceExplosion{}) - p.SetVelocity(p.Position().Sub(c.Pos).Normalize().Mul(impact)) + + v := p.Position().Sub(c.Pos).Normalize().Mul(impact) + p.KnockBack(c.Pos, impact, v[1]) } // protectionEnchantment represents an enchantment that can protect the player from damage. From 659a0ca3e2970595b3e63743a9716541503126f4 Mon Sep 17 00:00:00 2001 From: T14Raptor Date: Fri, 15 Jul 2022 21:17:21 -0400 Subject: [PATCH 16/41] block/explosion.go: Change Radius field back to Size. --- server/block/explosion.go | 10 +++++----- server/item/inventory/inventory.go | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/server/block/explosion.go b/server/block/explosion.go index 35e046c6a..8d9905782 100644 --- a/server/block/explosion.go +++ b/server/block/explosion.go @@ -20,8 +20,8 @@ type ExplosionConfig struct { World *world.World // Pos is the center position of the explosion. Pos mgl64.Vec3 - // Radius is the radius of the explosion. - Radius float64 + // Size is the size of the explosion, it is effectively the radius which entities/blocks will be affected within. + Size float64 // RandSource is the source to use for the explosion "randomness". RandSource rand.Source // SpawnFire will cause the explosion to randomly start fires in 1/3 of all destroyed air blocks that are @@ -78,7 +78,7 @@ func (c ExplosionConfig) Do() { c.RandSource = rand.NewSource(time.Now().UnixNano()) } - r, d := rand.New(c.RandSource), c.Radius*2 + r, d := rand.New(c.RandSource), c.Size*2 box := cube.Box( math.Floor(c.Pos[0]-d-1), math.Floor(c.Pos[1]-d-1), @@ -106,7 +106,7 @@ func (c ExplosionConfig) Do() { affectedBlocks := make([]cube.Pos, 0, 32) for _, ray := range rays { pos := c.Pos - for blastForce := c.Radius * (0.7 + r.Float64()*0.6); blastForce > 0.0; blastForce -= 0.225 { + for blastForce := c.Size * (0.7 + r.Float64()*0.6); blastForce > 0.0; blastForce -= 0.225 { current := cube.PosFromVec3(pos) if r, ok := c.World.Block(current).(Breakable); ok { if blastForce -= (r.BreakInfo().BlastResistance + 0.3) * 0.3; blastForce > 0 { @@ -122,7 +122,7 @@ func (c ExplosionConfig) Do() { explodable.Explode(pos, c) } else if breakable, ok := bl.(Breakable); ok { c.World.SetBlock(pos, nil, nil) - if 1/c.Radius > r.Float64() { + if 1/c.Size > r.Float64() { for _, drop := range breakable.BreakInfo().Drops(item.ToolNone{}, nil) { dropItem(c.World, drop, pos.Vec3Centre()) } diff --git a/server/item/inventory/inventory.go b/server/item/inventory/inventory.go index 7373dfee0..67fc9acfd 100644 --- a/server/item/inventory/inventory.go +++ b/server/item/inventory/inventory.go @@ -25,7 +25,7 @@ type Inventory struct { // ErrSlotOutOfRange is returned by any methods on inventory when a slot is passed which is not within the // range of valid values for the inventory. -var ErrSlotOutOfRange = errors.New("slot is out of range: must be in range 0 <= slot < inventory.Radius()") +var ErrSlotOutOfRange = errors.New("slot is out of range: must be in range 0 <= slot < inventory.Size()") // New creates a new inventory with the size passed. The inventory size cannot be changed after it has been // constructed. @@ -44,7 +44,7 @@ func New(size int, f func(slot int, item item.Stack)) *Inventory { // Item attempts to obtain an item from a specific slot in the inventory. If an item was present in that slot, // the item is returned and the error is nil. If no item was present in the slot, a Stack with air as its item // and a count of 0 is returned. Stack.Empty() may be called to check if this is the case. -// Item only returns an error if the slot passed is out of range. (0 <= slot < inventory.Radius()) +// Item only returns an error if the slot passed is out of range. (0 <= slot < inventory.Size()) func (inv *Inventory) Item(slot int) (item.Stack, error) { inv.check() if !inv.validSlot(slot) { @@ -59,7 +59,7 @@ func (inv *Inventory) Item(slot int) (item.Stack, error) { // SetItem sets a stack of items to a specific slot in the inventory. If an item is already present in the // slot, that item will be overwritten. -// SetItem will return an error if the slot passed is out of range. (0 <= slot < inventory.Radius()) +// SetItem will return an error if the slot passed is out of range. (0 <= slot < inventory.Size()) func (inv *Inventory) SetItem(slot int, item item.Stack) error { inv.check() if !inv.validSlot(slot) { From e5e5fa9d3840b630b1d2e27307a3501215d76738 Mon Sep 17 00:00:00 2001 From: T14Raptor Date: Sat, 16 Jul 2022 01:16:44 -0400 Subject: [PATCH 17/41] Fixes --- server/block/explosion.go | 12 ++++++------ server/entity/arrow.go | 4 +++- server/entity/ender_pearl.go | 4 +++- server/entity/snowball.go | 4 +++- server/entity/splash_potion.go | 4 +++- server/player/player.go | 6 +++--- 6 files changed, 21 insertions(+), 13 deletions(-) diff --git a/server/block/explosion.go b/server/block/explosion.go index 8d9905782..fa6dd738c 100644 --- a/server/block/explosion.go +++ b/server/block/explosion.go @@ -94,7 +94,7 @@ func (c ExplosionConfig) Do() { continue } dist := pos.Sub(c.Pos).Len() - if dist > d { + if dist >= d { continue } if explodable, ok := e.(ExplodableEntity); ok { @@ -160,18 +160,18 @@ func exposure(origin mgl64.Vec3, e world.Entity) float64 { double7 := (1.0 - math.Floor(diff[0])/diff[0]) / 2.0 double8 := (1.0 - math.Floor(diff[2])/diff[2]) / 2.0 - var collisions, checks float64 + var collisions, checks int for x := 0.0; x <= 1.0; x += step[0] { for y := 0.0; y <= 1.0; y += step[1] { for z := 0.0; z <= 1.0; z += step[2] { - dck2 := mgl64.Vec3{ + point := mgl64.Vec3{ lerp(x, boxMin[0], boxMax[0]) + double7, lerp(y, boxMin[1], boxMax[1]), lerp(z, boxMin[2], boxMax[2]) + double8, } var collides bool - trace.TraverseBlocks(origin, dck2, func(pos cube.Pos) (con bool) { + trace.TraverseBlocks(origin, point, func(pos cube.Pos) (con bool) { _, air := w.Block(pos).(Air) collides = !air return air @@ -183,10 +183,10 @@ func exposure(origin mgl64.Vec3, e world.Entity) float64 { } } } - return collisions / checks + return float64(collisions) / float64(checks) } // lerp returns the linear interpolation between a and b at t. func lerp(a, b, t float64) float64 { - return (1-t)*a + t*b + return b + a*(t-b) } diff --git a/server/entity/arrow.go b/server/entity/arrow.go index 370297b2d..762fa1847 100644 --- a/server/entity/arrow.go +++ b/server/entity/arrow.go @@ -235,7 +235,9 @@ func (a *Arrow) New(pos, vel mgl64.Vec3, yaw, pitch float64, owner world.Entity, // Explode ... func (a *Arrow) Explode(c block.ExplosionConfig, impact float64) { - a.SetVelocity(a.Position().Sub(c.Pos).Normalize().Mul(impact)) + a.mu.Lock() + a.vel = a.vel.Add(a.pos.Sub(c.Pos).Normalize().Mul(impact)) + a.mu.Unlock() } // Owner returns the world.Entity that fired the Arrow, or nil if it did not have any. diff --git a/server/entity/ender_pearl.go b/server/entity/ender_pearl.go index 1864b8d1b..36134a036 100644 --- a/server/entity/ender_pearl.go +++ b/server/entity/ender_pearl.go @@ -134,7 +134,9 @@ func (e *EnderPearl) New(pos, vel mgl64.Vec3, yaw, pitch float64) world.Entity { // Explode ... func (e *EnderPearl) Explode(c block.ExplosionConfig, impact float64) { - e.SetVelocity(e.Position().Sub(c.Pos).Normalize().Mul(impact)) + e.mu.Lock() + e.vel = e.vel.Add(e.pos.Sub(c.Pos).Normalize().Mul(impact)) + e.mu.Unlock() } // Owner ... diff --git a/server/entity/snowball.go b/server/entity/snowball.go index 817411d62..921e34947 100644 --- a/server/entity/snowball.go +++ b/server/entity/snowball.go @@ -115,7 +115,9 @@ func (s *Snowball) New(pos, vel mgl64.Vec3, yaw, pitch float64) world.Entity { // Explode ... func (s *Snowball) Explode(c block.ExplosionConfig, impact float64) { - s.SetVelocity(s.Position().Sub(c.Pos).Normalize().Mul(impact)) + s.mu.Lock() + s.vel = s.vel.Add(s.pos.Sub(c.Pos).Normalize().Mul(impact)) + s.mu.Unlock() } // Owner ... diff --git a/server/entity/splash_potion.go b/server/entity/splash_potion.go index f0fc5ec06..276a9d476 100644 --- a/server/entity/splash_potion.go +++ b/server/entity/splash_potion.go @@ -179,7 +179,9 @@ func (s *SplashPotion) New(pos, vel mgl64.Vec3, yaw, pitch float64, t potion.Pot // Explode ... func (s *SplashPotion) Explode(c block.ExplosionConfig, impact float64) { - s.SetVelocity(s.Position().Sub(c.Pos).Normalize().Mul(impact)) + s.mu.Lock() + s.vel = s.vel.Add(s.pos.Sub(c.Pos).Normalize().Mul(impact)) + s.mu.Unlock() } // Owner ... diff --git a/server/player/player.go b/server/player/player.go index 3a2918d9e..1ba26762b 100644 --- a/server/player/player.go +++ b/server/player/player.go @@ -621,10 +621,10 @@ func (p *Player) FinalDamageFrom(dmg float64, src damage.Source) float64 { // Explode ... func (p *Player) Explode(c block.ExplosionConfig, impact float64) { - p.Hurt(math.Floor((impact*impact+impact)*3.5*c.Radius+1), damage.SourceExplosion{}) + p.Hurt(math.Floor((impact*impact+impact)*3.5*c.Size+1), damage.SourceExplosion{}) - v := p.Position().Sub(c.Pos).Normalize().Mul(impact) - p.KnockBack(c.Pos, impact, v[1]) + diff := p.Position().Sub(c.Pos) + p.KnockBack(c.Pos, impact, diff[1]/diff.Len()*impact) } // protectionEnchantment represents an enchantment that can protect the player from damage. From cc795311abb5f4a73f1b2e79cc01b39930fd4e16 Mon Sep 17 00:00:00 2001 From: T14Raptor Date: Mon, 18 Jul 2022 08:55:15 -0400 Subject: [PATCH 18/41] block/explosions.go: Fixed exposure? --- main.go | 23 +---------------------- server/block/break_info.go | 4 ++-- server/block/explosion.go | 23 +++++++++++------------ 3 files changed, 14 insertions(+), 36 deletions(-) diff --git a/main.go b/main.go index 59fb321e5..ce94041e2 100644 --- a/main.go +++ b/main.go @@ -3,11 +3,7 @@ package main import ( "fmt" "github.com/df-mc/dragonfly/server" - "github.com/df-mc/dragonfly/server/block" - "github.com/df-mc/dragonfly/server/event" - "github.com/df-mc/dragonfly/server/player" "github.com/df-mc/dragonfly/server/player/chat" - "github.com/df-mc/dragonfly/server/world" "github.com/pelletier/go-toml" "github.com/sirupsen/logrus" "io/ioutil" @@ -32,27 +28,10 @@ func main() { log.Fatalln(err) } - for srv.Accept(func(p *player.Player) { - p.SetGameMode(world.GameModeSurvival) - p.Handle(h{p: p}) - }) { + for srv.Accept(nil) { } } -type h struct { - player.NopHandler - - p *player.Player -} - -func (h h) HandleChat(*event.Context, *string) { - block.ExplosionConfig{ - World: h.p.World(), - Pos: h.p.Position(), - Radius: 4, - }.Do() -} - // readConfig reads the configuration from the config.toml file, or creates the file if it does not yet exist. func readConfig() (server.Config, error) { c := server.DefaultConfig() diff --git a/server/block/break_info.go b/server/block/break_info.go index 0d967f2b9..211dfe741 100644 --- a/server/block/break_info.go +++ b/server/block/break_info.go @@ -91,11 +91,11 @@ type BreakInfo struct { } // newBreakInfo creates a BreakInfo struct with the properties passed. The XPDrops field is 0 by default. The blast -// resistance is set to the hardness by default, as it usually is the same. +// resistance is set to the block's hardness*5 by default. func newBreakInfo(hardness float64, harvestable func(item.Tool) bool, effective func(item.Tool) bool, drops func(item.Tool, []item.Enchantment) []item.Stack) BreakInfo { return BreakInfo{ Hardness: hardness, - BlastResistance: hardness, + BlastResistance: hardness * 5, Harvestable: harvestable, Effective: effective, Drops: drops, diff --git a/server/block/explosion.go b/server/block/explosion.go index fa6dd738c..63cfb4831 100644 --- a/server/block/explosion.go +++ b/server/block/explosion.go @@ -98,8 +98,7 @@ func (c ExplosionConfig) Do() { continue } if explodable, ok := e.(ExplodableEntity); ok { - // TODO: exposure - explodable.Explode(c, 1-dist/d) + explodable.Explode(c, (1-dist/d)*exposure(c.Pos, e)) } } @@ -157,33 +156,33 @@ func exposure(origin mgl64.Vec3, e world.Entity) float64 { return 0.0 } - double7 := (1.0 - math.Floor(diff[0])/diff[0]) / 2.0 - double8 := (1.0 - math.Floor(diff[2])/diff[2]) / 2.0 + xOffset := (1.0 - math.Floor(diff[0])/diff[0]) / 2.0 + zOffset := (1.0 - math.Floor(diff[2])/diff[2]) / 2.0 - var collisions, checks int + var checks, misses int for x := 0.0; x <= 1.0; x += step[0] { for y := 0.0; y <= 1.0; y += step[1] { for z := 0.0; z <= 1.0; z += step[2] { point := mgl64.Vec3{ - lerp(x, boxMin[0], boxMax[0]) + double7, + lerp(x, boxMin[0], boxMax[0]) + xOffset, lerp(y, boxMin[1], boxMax[1]), - lerp(z, boxMin[2], boxMax[2]) + double8, + lerp(z, boxMin[2], boxMax[2]) + zOffset, } - var collides bool + var collided bool trace.TraverseBlocks(origin, point, func(pos cube.Pos) (con bool) { _, air := w.Block(pos).(Air) - collides = !air + collided = !air return air }) - if collides { - collisions++ + if !collided { + misses++ } checks++ } } } - return float64(collisions) / float64(checks) + return float64(misses) / float64(checks) } // lerp returns the linear interpolation between a and b at t. From c4f7ec80499e15f854213e29d8e496580da17818 Mon Sep 17 00:00:00 2001 From: JustTal Date: Mon, 25 Jul 2022 22:17:37 -0500 Subject: [PATCH 19/41] block/block.go: Blame GitHub! --- server/block/block.go | 1 - 1 file changed, 1 deletion(-) diff --git a/server/block/block.go b/server/block/block.go index 3579fb43e..3438f9f6f 100644 --- a/server/block/block.go +++ b/server/block/block.go @@ -3,7 +3,6 @@ package block import ( "github.com/df-mc/dragonfly/server/block/cube" "github.com/df-mc/dragonfly/server/block/model" - "github.com/df-mc/dragonfly/server/entity" "github.com/df-mc/dragonfly/server/entity/damage" "github.com/df-mc/dragonfly/server/item" "github.com/df-mc/dragonfly/server/world" From cb209b123e869e9a2089c9e30643f790ad03ce5e Mon Sep 17 00:00:00 2001 From: T14Raptor Date: Mon, 25 Jul 2022 23:41:47 -0400 Subject: [PATCH 20/41] block/explosion.go: Divide blast resistance by 5. --- server/block/explosion.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/block/explosion.go b/server/block/explosion.go index 63cfb4831..15af65edb 100644 --- a/server/block/explosion.go +++ b/server/block/explosion.go @@ -108,7 +108,7 @@ func (c ExplosionConfig) Do() { for blastForce := c.Size * (0.7 + r.Float64()*0.6); blastForce > 0.0; blastForce -= 0.225 { current := cube.PosFromVec3(pos) if r, ok := c.World.Block(current).(Breakable); ok { - if blastForce -= (r.BreakInfo().BlastResistance + 0.3) * 0.3; blastForce > 0 { + if blastForce -= (r.BreakInfo().BlastResistance/5 + 0.3) * 0.3; blastForce > 0 { affectedBlocks = append(affectedBlocks, current) } } From c29385b761353e07215a8644c36a3a994e5745a4 Mon Sep 17 00:00:00 2001 From: T14Raptor Date: Tue, 26 Jul 2022 00:26:38 -0400 Subject: [PATCH 21/41] player/player.go: Fixed explosion knockback when in creative. --- server/player/player.go | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/server/player/player.go b/server/player/player.go index f76319a10..92904ede1 100644 --- a/server/player/player.go +++ b/server/player/player.go @@ -659,8 +659,15 @@ func (p *Player) FinalDamageFrom(dmg float64, src damage.Source) float64 { func (p *Player) Explode(c block.ExplosionConfig, impact float64) { p.Hurt(math.Floor((impact*impact+impact)*3.5*c.Size+1), damage.SourceExplosion{}) - diff := p.Position().Sub(c.Pos) - p.KnockBack(c.Pos, impact, diff[1]/diff.Len()*impact) + vel := p.Position().Sub(c.Pos).Normalize().Mul(impact) + res := 0.0 + for _, i := range p.armour.Items() { + if a, ok := i.Item().(item.Armour); ok { + res += a.KnockBackResistance() + } + } + + p.SetVelocity(vel.Mul(1 - res)) } // protectionEnchantment represents an enchantment that can protect the player from damage. From 3d79496a3d7e3d72c53c813b8dc57748072008d2 Mon Sep 17 00:00:00 2001 From: JustTal Date: Wed, 27 Jul 2022 16:10:21 -0500 Subject: [PATCH 22/41] dragonfly: Implemented beds. --- server/block/bed.go | 208 ++++++++++++++++++++ server/block/farmland.go | 2 + server/block/hash.go | 5 + server/block/model/bed.go | 18 ++ server/block/register.go | 2 + server/player/player.go | 62 +++++- server/session/controllable.go | 3 + server/session/entity_metadata.go | 32 ++- server/session/handler_player_action.go | 4 + server/session/handler_player_auth_input.go | 2 +- server/session/session.go | 4 +- server/session/world.go | 25 ++- server/world/settings.go | 2 + server/world/sleep.go | 49 +++++ server/world/tick.go | 10 + server/world/viewer.go | 3 + server/world/world.go | 23 +++ 17 files changed, 441 insertions(+), 13 deletions(-) create mode 100644 server/block/bed.go create mode 100644 server/block/model/bed.go create mode 100644 server/world/sleep.go diff --git a/server/block/bed.go b/server/block/bed.go new file mode 100644 index 000000000..1091102f7 --- /dev/null +++ b/server/block/bed.go @@ -0,0 +1,208 @@ +package block + +import ( + "github.com/df-mc/dragonfly/server/block/cube" + "github.com/df-mc/dragonfly/server/block/model" + "github.com/df-mc/dragonfly/server/internal/nbtconv" + "github.com/df-mc/dragonfly/server/item" + "github.com/df-mc/dragonfly/server/world" + "github.com/go-gl/mathgl/mgl64" + "github.com/google/uuid" + "github.com/sandertv/gophertunnel/minecraft/text" +) + +// Bed is a block, allowing players to sleep to set their spawns and skip the night. +type Bed struct { + transparent + + // Colour is the colour of the bed. + Colour item.Colour + // Facing is the direction that the bed is facing. + Facing cube.Direction + // Head is true if the bed is the head side or the bottom side. + Head bool + // Occupied is true if the bed is occupied by another player. + Occupied bool +} + +// Model ... +func (Bed) Model() world.BlockModel { + return model.Bed{} +} + +// BreakInfo ... +func (b Bed) BreakInfo() BreakInfo { + return newBreakInfo(0.2, alwaysHarvestable, axeEffective, oneOf(b)) +} + +// UseOnBlock ... +func (b Bed) UseOnBlock(pos cube.Pos, face cube.Face, _ mgl64.Vec3, w *world.World, user item.User, ctx *item.UseContext) (used bool) { + if pos, _, used = firstReplaceable(w, pos, face, b); !used { + return + } + if _, ok := w.Block(pos.Side(cube.FaceDown)).Model().(model.Solid); !ok { + return + } + + b.Facing = user.Facing() + + side, sidePos := b, pos.Side(b.Facing.Face()) + side.Head = true + + if !replaceableWith(w, sidePos, side) { + return + } + if _, ok := w.Block(sidePos.Side(cube.FaceDown)).Model().(model.Solid); !ok { + return + } + + place(w, sidePos, side, user, ctx) + place(w, pos, b, user, ctx) + return placed(ctx) +} + +// sleeper represents an entity that can sleep on beds. +type sleeper interface { + item.User + UUID() uuid.UUID + Message(a ...any) + Sleep(pos cube.Pos) +} + +// Activate ... +func (b Bed) Activate(pos cube.Pos, _ cube.Face, w *world.World, u item.User, _ *item.UseContext) bool { + s, ok := u.(sleeper) + if !ok { + return false + } + + if w.Dimension() != world.Overworld { + w.SetBlock(pos, nil, nil) + ExplosionConfig{ + World: w, + Pos: pos.Vec3Centre(), + + Size: 5, + SpawnFire: true, + }.Do() + return true + } + + side, sidePos, ok := b.side(pos, w) + if !ok { + return false + } + + userPos := s.Position() + if sidePos.Vec3Middle().Sub(userPos).Len() > 4 && pos.Vec3Middle().Sub(userPos).Len() > 4 { + s.Message(text.Colourf("Bed is too far away")) + return true + } + + headSide, headPos := side, sidePos + if b.Head { + headSide, headPos = b, pos + } + + w.SetPlayerSpawn(s.UUID(), headPos) + s.Message(text.Colourf("Respawn point set")) + + time := w.Time() % world.TimeFull + if !(time >= world.TimeNight && time < world.TimeSunrise) { + s.Message(text.Colourf("You can only sleep at night")) + return true + } + if headSide.Occupied { + s.Message(text.Colourf("This bed is occupied")) + return true + } + + s.Sleep(headPos) + return true +} + +// EntityLand ... +func (b Bed) EntityLand(_ cube.Pos, _ *world.World, e world.Entity) { + if s, ok := e.(sneakingEntity); ok && s.Sneaking() { + // If the entity is sneaking, the fall distance and velocity stay the same. + return + } + if f, ok := e.(fallDistanceEntity); ok { + f.SetFallDistance(f.FallDistance() * 0.5) + } + if v, ok := e.(velocityEntity); ok { + vel := v.Velocity() + vel[1] = vel[1] * -3 / 4 + v.SetVelocity(vel) + } +} + +// sneakingEntity represents an entity that can sneak. +type sneakingEntity interface { + // Sneaking returns true if the entity is currently sneaking. + Sneaking() bool +} + +// velocityEntity represents an entity that can maintain a velocity. +type velocityEntity interface { + // Velocity returns the current velocity of the entity. + Velocity() mgl64.Vec3 + // SetVelocity sets the velocity of the entity. + SetVelocity(mgl64.Vec3) +} + +// NeighbourUpdateTick ... +func (b Bed) NeighbourUpdateTick(pos, _ cube.Pos, w *world.World) { + if _, _, ok := b.side(pos, w); !ok { + w.SetBlock(pos, nil, nil) + } +} + +// EncodeItem ... +func (b Bed) EncodeItem() (name string, meta int16) { + return "minecraft:bed", int16(b.Colour.Uint8()) +} + +// EncodeBlock ... +func (b Bed) EncodeBlock() (name string, properties map[string]interface{}) { + return "minecraft:bed", map[string]interface{}{ + "facing_bit": int32(horizontalDirection(b.Facing)), + "occupied_bit": boolByte(b.Occupied), + "head_bit": boolByte(b.Head), + } +} + +// EncodeNBT ... +func (b Bed) EncodeNBT() map[string]interface{} { + return map[string]interface{}{ + "id": "Bed", + "color": b.Colour.Uint8(), + } +} + +// DecodeNBT ... +func (b Bed) DecodeNBT(data map[string]interface{}) interface{} { + b.Colour = item.Colours()[nbtconv.Map[uint8](data, "color")] + return b +} + +// side returns the other side of the bed. If the other side is not a bed, the third return value is false. +func (b Bed) side(pos cube.Pos, w *world.World) (Bed, cube.Pos, bool) { + face := b.Facing.Face() + if b.Head { + face = face.Opposite() + } + + sidePos := pos.Side(face) + o, ok := w.Block(sidePos).(Bed) + return o, sidePos, ok +} + +// allBeds returns all possible beds. +func allBeds() (beds []world.Block) { + for _, d := range cube.Directions() { + beds = append(beds, Bed{Facing: d}) + beds = append(beds, Bed{Facing: d, Head: true}) + } + return +} diff --git a/server/block/farmland.go b/server/block/farmland.go index 54c2027ce..b2391ed04 100644 --- a/server/block/farmland.go +++ b/server/block/farmland.go @@ -81,6 +81,8 @@ func (f Farmland) EntityLand(pos cube.Pos, w *world.World, e world.Entity, dista type fallDistanceEntity interface { // ResetFallDistance resets the entities fall distance. ResetFallDistance() + // SetFallDistance sets the entities fall distance. + SetFallDistance(float64) // FallDistance returns the entities fall distance. FallDistance() float64 } diff --git a/server/block/hash.go b/server/block/hash.go index c6b6337d1..5add3d5f7 100644 --- a/server/block/hash.go +++ b/server/block/hash.go @@ -12,6 +12,7 @@ const ( hashBarrier hashBasalt hashBeacon + hashBed hashBedrock hashBeetrootSeeds hashBlastFurnace @@ -198,6 +199,10 @@ func (Beacon) Hash() uint64 { return hashBeacon } +func (b Bed) Hash() uint64 { + return hashBed | uint64(b.Facing)<<8 | uint64(boolByte(b.Head))<<10 +} + func (b Bedrock) Hash() uint64 { return hashBedrock | uint64(boolByte(b.InfiniteBurning))<<8 } diff --git a/server/block/model/bed.go b/server/block/model/bed.go new file mode 100644 index 000000000..f641aa7de --- /dev/null +++ b/server/block/model/bed.go @@ -0,0 +1,18 @@ +package model + +import ( + "github.com/df-mc/dragonfly/server/block/cube" + "github.com/df-mc/dragonfly/server/world" +) + +// Bed is a model used for beds. This model works for both parts of the bed. +type Bed struct{} + +func (b Bed) BBox(cube.Pos, *world.World) []cube.BBox { + return []cube.BBox{cube.Box(0, 0, 0, 1, 0.5625, 1)} +} + +// FaceSolid ... +func (Bed) FaceSolid(cube.Pos, cube.Face, *world.World) bool { + return false +} diff --git a/server/block/register.go b/server/block/register.go index 61eb9b69f..20a96368f 100644 --- a/server/block/register.go +++ b/server/block/register.go @@ -108,6 +108,7 @@ func init() { registerAll(allAnvils()) registerAll(allBarrels()) registerAll(allBasalt()) + registerAll(allBeds()) registerAll(allBeetroot()) registerAll(allBlastFurnaces()) registerAll(allBoneBlock()) @@ -339,6 +340,7 @@ func init() { world.RegisterItem(Anvil{Type: t}) } for _, c := range item.Colours() { + world.RegisterItem(Bed{Colour: c}) world.RegisterItem(Carpet{Colour: c}) world.RegisterItem(ConcretePowder{Colour: c}) world.RegisterItem(Concrete{Colour: c}) diff --git a/server/player/player.go b/server/player/player.go index 92904ede1..b093b96fe 100644 --- a/server/player/player.go +++ b/server/player/player.go @@ -68,6 +68,9 @@ type Player struct { invisible, immobile, onGround, usingItem atomic.Bool usingSince atomic.Int64 + sleeping atomic.Bool + sleepPos atomic.Value[cube.Pos] + fireTicks atomic.Int64 fallDistance atomic.Float64 @@ -267,7 +270,12 @@ func (p *Player) SendToast(title, message string) { // ResetFallDistance resets the player's fall distance. func (p *Player) ResetFallDistance() { - p.fallDistance.Store(0) + p.SetFallDistance(0) +} + +// SetFallDistance sets the player's fall distance. +func (p *Player) SetFallDistance(distance float64) { + p.fallDistance.Store(distance) } // FallDistance returns the player's fall distance. @@ -1052,6 +1060,53 @@ func (p *Player) StopFlying() { p.session().SendGameMode(p.GameMode()) } +// Sleep makes the player sleep at the given position. If the position does not map to a bed (specifically the head side), +// the player will not sleep. +func (p *Player) Sleep(pos cube.Pos) { + w := p.World() + b, ok := w.Block(pos).(block.Bed) + if !ok || b.Occupied || !b.Head { + // The player cannot sleep here. + return + } + b.Occupied = true + w.SetBlock(pos, b, nil) + w.SetSleepRequirement(time.Second * 5) + + p.sleeping.Store(true) + p.sleepPos.Store(pos) + p.updateState() +} + +// Wake forces the player out of bed if they are sleeping. +func (p *Player) Wake() { + if !p.sleeping.CAS(true, false) { + return + } + + w := p.World() + w.SetSleepRequirement(0) + for _, v := range w.Viewers(p.Position()) { + v.ViewEntityWake(p) + } + p.updateState() + + pos := p.sleepPos.Load() + if b, ok := w.Block(pos).(block.Bed); ok { + b.Occupied = false + w.SetBlock(pos, b, nil) + } +} + +// Sleeping returns true if the player is currently sleeping, along with the position of the bed the player is sleeping +// on. +func (p *Player) Sleeping() (cube.Pos, bool) { + if !p.sleeping.Load() { + return cube.Pos{}, false + } + return p.sleepPos.Load(), true +} + // Jump makes the player jump if they are on ground. It exhausts the player by 0.05 food points, an additional 0.15 // is exhausted if the player is sprint jumping. func (p *Player) Jump() { @@ -1954,6 +2009,8 @@ func (p *Player) Move(deltaPos mgl64.Vec3, deltaYaw, deltaPitch float64) { p.yaw.Store(resYaw) p.pitch.Store(resPitch) + p.vel.Store(deltaPos) + _, submergedBefore := w.Liquid(cube.PosFromVec3(pos.Add(mgl64.Vec3{0, p.EyeHeight()}))) _, submergedAfter := w.Liquid(cube.PosFromVec3(res.Add(mgl64.Vec3{0, p.EyeHeight()}))) if submergedBefore != submergedAfter { @@ -2001,8 +2058,9 @@ func (p *Player) Velocity() mgl64.Vec3 { // SetVelocity updates the player's velocity. If there is an attached session, this will just send // the velocity to the player session for the player to update. func (p *Player) SetVelocity(velocity mgl64.Vec3) { + p.vel.Store(velocity) if p.session() == session.Nop { - p.vel.Store(velocity) + // We don't have a session, so we don't need to send the velocity here. return } for _, v := range p.viewers() { diff --git a/server/session/controllable.go b/server/session/controllable.go index 9dba003c7..1e49008bc 100644 --- a/server/session/controllable.go +++ b/server/session/controllable.go @@ -32,6 +32,9 @@ type Controllable interface { Move(deltaPos mgl64.Vec3, deltaYaw, deltaPitch float64) Speed() float64 + Sleep(pos cube.Pos) + Wake() + Chat(msg ...any) ExecuteCommand(commandLine string) GameMode() world.GameMode diff --git a/server/session/entity_metadata.go b/server/session/entity_metadata.go index f2bd6190d..ab17b8a94 100644 --- a/server/session/entity_metadata.go +++ b/server/session/entity_metadata.go @@ -1,6 +1,7 @@ package session import ( + "github.com/df-mc/dragonfly/server/block/cube" "github.com/df-mc/dragonfly/server/entity/effect" "github.com/df-mc/dragonfly/server/internal/nbtconv" "github.com/df-mc/dragonfly/server/item/potion" @@ -79,6 +80,12 @@ func (s *Session) parseEntityMetadata(e world.Entity) entityMetadata { if sc, ok := e.(scoreTag); ok { m[dataKeyScoreTag] = sc.ScoreTag() } + if sl, ok := e.(sleeper); ok { + if pos, ok := sl.Sleeping(); ok { + m[dataKeyBedPosition] = blockPosToProtocol(pos) + m.setFlag(dataKeyPlayerFlags, dataPlayerFlagSleep) + } + } if sp, ok := e.(splash); ok { pot := sp.Type() m[dataKeyPotionAuxValue] = int16(pot.Uint8()) @@ -108,10 +115,19 @@ func (s *Session) parseEntityMetadata(e world.Entity) entityMetadata { // setFlag sets a flag with a specific index in the int64 stored in the entity metadata map to the value // passed. It is typically used for entity metadata flags. func (m entityMetadata) setFlag(key uint32, index uint8) { - if v, ok := m[key]; !ok { - m[key] = int64(0) ^ (1 << uint64(index)) + v, ok := m[key] + if key == dataKeyPlayerFlags { + if ok { + m[key] = v.(byte) ^ 1<= TimeNight && timeOfDay < TimeSunrise { + t.w.SetTime(totalTime + TimeFull - timeOfDay) + t.w.StopRaining() + for _, s := range sleepers { + s.Wake() + } + } +} diff --git a/server/world/tick.go b/server/world/tick.go index 130aed965..54dae38c4 100644 --- a/server/world/tick.go +++ b/server/world/tick.go @@ -52,6 +52,13 @@ func (t ticker) tick() { } rain, thunder, tick, tim := t.w.set.Raining, t.w.set.Thundering && t.w.set.Raining, t.w.set.CurrentTick, int(t.w.set.Time) + sleep := false + if t.w.set.RequiredSleepTicks > 0 { + t.w.set.RequiredSleepTicks-- + if t.w.set.RequiredSleepTicks-1 <= 0 { + sleep = true + } + } t.w.set.Unlock() if tick%20 == 0 { @@ -64,6 +71,9 @@ func (t ticker) tick() { } } } + if sleep { + t.tryAdvanceDay() + } if thunder { t.w.tickLightning() } diff --git a/server/world/viewer.go b/server/world/viewer.go index 544daed0c..7d6b23575 100644 --- a/server/world/viewer.go +++ b/server/world/viewer.go @@ -63,6 +63,8 @@ type Viewer interface { ViewWorldSpawn(pos cube.Pos) // ViewWeather views the weather of the world, including rain and thunder. ViewWeather(raining, thunder bool) + // ViewEntityWake views an entity wake up from a bed. + ViewEntityWake(e Entity) } // NopViewer is a Viewer implementation that does not implement any behaviour. It may be embedded by other structs to @@ -91,5 +93,6 @@ func (NopViewer) ViewEmote(Entity, uuid.UUID) func (NopViewer) ViewSkin(Entity) {} func (NopViewer) ViewWorldSpawn(cube.Pos) {} func (NopViewer) ViewWeather(bool, bool) {} +func (NopViewer) ViewEntityWake(Entity) {} func (NopViewer) ViewFurnaceUpdate(time.Duration, time.Duration, time.Duration, time.Duration, time.Duration, time.Duration) { } diff --git a/server/world/world.go b/server/world/world.go index 0fbce9202..e2e4db5a0 100644 --- a/server/world/world.go +++ b/server/world/world.go @@ -64,6 +64,16 @@ type World struct { viewers map[*Loader]Viewer } +const ( + TimeDay = 1000 + TimeNoon = 6000 + TimeSunset = 12000 + TimeNight = 13000 + TimeMidnight = 18000 + TimeSunrise = 23000 + TimeFull = 24000 +) + // New creates a new initialised world. The world may be used right away, but it will not be saved or loaded // from files until it has been given a different provider than the default. (NopProvider) // By default, the name of the world will be 'World'. @@ -719,6 +729,8 @@ func (w *World) RemoveEntity(e Entity) { viewers := slices.Clone(c.v) c.Unlock() + w.tryAdvanceDay() + w.entityMu.Lock() delete(w.entities, e) w.entityMu.Unlock() @@ -844,6 +856,17 @@ func (w *World) SetPlayerSpawn(uuid uuid.UUID, pos cube.Pos) { } } +// SetSleepRequirement sets the duration of time players in the world must sleep for, in order for the time to change to +// day. +func (w *World) SetSleepRequirement(duration time.Duration) { + if w == nil { + return + } + w.set.Lock() + defer w.set.Unlock() + w.set.RequiredSleepTicks = duration.Milliseconds() / 50 +} + // DefaultGameMode returns the default game mode of the world. When players join, they are given this game // mode. // The default game mode may be changed using SetDefaultGameMode(). From f0066e07a9641380379eaf8d5d353d8b2867b504 Mon Sep 17 00:00:00 2001 From: JustTal Date: Wed, 27 Jul 2022 17:45:30 -0500 Subject: [PATCH 23/41] player/player.go: Various fixes. --- server/player/player.go | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/server/player/player.go b/server/player/player.go index b093b96fe..493c02938 100644 --- a/server/player/player.go +++ b/server/player/player.go @@ -1065,7 +1065,7 @@ func (p *Player) StopFlying() { func (p *Player) Sleep(pos cube.Pos) { w := p.World() b, ok := w.Block(pos).(block.Bed) - if !ok || b.Occupied || !b.Head { + if ok && b.Occupied { // The player cannot sleep here. return } @@ -1086,7 +1086,8 @@ func (p *Player) Wake() { w := p.World() w.SetSleepRequirement(0) - for _, v := range w.Viewers(p.Position()) { + + for _, v := range p.viewers() { v.ViewEntityWake(p) } p.updateState() @@ -1958,6 +1959,8 @@ func (p *Player) Teleport(pos mgl64.Vec3) { if p.Handler().HandleTeleport(ctx, pos); ctx.Cancelled() { return } + p.Wake() + p.ResetFallDistance() p.teleport(pos) } @@ -2524,9 +2527,14 @@ func (p *Player) checkOnGround(w *world.World) bool { // BBox returns the axis aligned bounding box of the player. func (p *Player) BBox() cube.BBox { s := p.Scale() - switch { + // TODO: Shrink BBox for sneaking once implemented in Bedrock Edition. This is already a thing in Java Edition. - case p.Swimming(): + swimming := p.Swimming() + _, sleeping := p.Sleeping() + switch { + case sleeping: + return cube.Box(-0.1*s, 0, -0.1*s, 0.1*s, 0.2*s, 0.1*s) + case 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) From 1ad385d84acd3151074fdf202bc1927bd4549805 Mon Sep 17 00:00:00 2001 From: JustTal Date: Wed, 27 Jul 2022 23:54:33 -0500 Subject: [PATCH 24/41] player/player.go: Various fixes. --- server/block/bed.go | 40 ++++++++++++++++++++----- server/block/break_info.go | 7 +++++ server/player/player.go | 9 ++++-- server/session/entity_metadata.go | 21 +++++-------- server/session/handler_player_action.go | 5 +--- 5 files changed, 54 insertions(+), 28 deletions(-) diff --git a/server/block/bed.go b/server/block/bed.go index 1091102f7..991a2d496 100644 --- a/server/block/bed.go +++ b/server/block/bed.go @@ -21,8 +21,8 @@ type Bed struct { Facing cube.Direction // Head is true if the bed is the head side or the bottom side. Head bool - // Occupied is true if the bed is occupied by another player. - Occupied bool + // User is the user that is using the bed. + User item.User } // Model ... @@ -67,6 +67,7 @@ type sleeper interface { UUID() uuid.UUID Message(a ...any) Sleep(pos cube.Pos) + Wake() } // Activate ... @@ -88,7 +89,7 @@ func (b Bed) Activate(pos cube.Pos, _ cube.Face, w *world.World, u item.User, _ return true } - side, sidePos, ok := b.side(pos, w) + _, sidePos, ok := b.side(pos, w) if !ok { return false } @@ -99,9 +100,9 @@ func (b Bed) Activate(pos cube.Pos, _ cube.Face, w *world.World, u item.User, _ return true } - headSide, headPos := side, sidePos - if b.Head { - headSide, headPos = b, pos + headSide, headPos, ok := b.head(pos, w) + if !ok { + return false } w.SetPlayerSpawn(s.UUID(), headPos) @@ -112,7 +113,7 @@ func (b Bed) Activate(pos cube.Pos, _ cube.Face, w *world.World, u item.User, _ s.Message(text.Colourf("You can only sleep at night")) return true } - if headSide.Occupied { + if headSide.User != nil { s.Message(text.Colourf("This bed is occupied")) return true } @@ -121,6 +122,17 @@ func (b Bed) Activate(pos cube.Pos, _ cube.Face, w *world.World, u item.User, _ return true } +// PostBreak ... +func (b Bed) PostBreak(pos cube.Pos, w *world.World, _ item.User) { + headSide, _, ok := b.head(pos, w) + if !ok { + return + } + if s, ok := headSide.User.(sleeper); ok { + s.Wake() + } +} + // EntityLand ... func (b Bed) EntityLand(_ cube.Pos, _ *world.World, e world.Entity) { if s, ok := e.(sneakingEntity); ok && s.Sneaking() { @@ -167,7 +179,7 @@ func (b Bed) EncodeItem() (name string, meta int16) { func (b Bed) EncodeBlock() (name string, properties map[string]interface{}) { return "minecraft:bed", map[string]interface{}{ "facing_bit": int32(horizontalDirection(b.Facing)), - "occupied_bit": boolByte(b.Occupied), + "occupied_bit": boolByte(b.User != nil), "head_bit": boolByte(b.Head), } } @@ -186,6 +198,18 @@ func (b Bed) DecodeNBT(data map[string]interface{}) interface{} { return b } +// head returns the head side of the bed. If neither side is a head side, the third return value is false. +func (b Bed) head(pos cube.Pos, w *world.World) (Bed, cube.Pos, bool) { + headSide, headPos, ok := b.side(pos, w) + if !ok { + return Bed{}, cube.Pos{}, false + } + if b.Head { + headSide, headPos = b, pos + } + return headSide, headPos, true +} + // side returns the other side of the bed. If the other side is not a bed, the third return value is false. func (b Bed) side(pos cube.Pos, w *world.World) (Bed, cube.Pos, bool) { face := b.Facing.Face() diff --git a/server/block/break_info.go b/server/block/break_info.go index 211dfe741..06953e54d 100644 --- a/server/block/break_info.go +++ b/server/block/break_info.go @@ -1,6 +1,7 @@ package block import ( + "github.com/df-mc/dragonfly/server/block/cube" "github.com/df-mc/dragonfly/server/item" "github.com/df-mc/dragonfly/server/item/enchantment" "github.com/df-mc/dragonfly/server/world" @@ -15,6 +16,12 @@ type Breakable interface { BreakInfo() BreakInfo } +// PostBreakable represents a block that has an action(s) processed after the block is broken. +type PostBreakable interface { + // PostBreak is called after the block has broken. + PostBreak(pos cube.Pos, w *world.World, u item.User) +} + // BreakDuration returns the base duration that breaking the block passed takes when being broken using the // item passed. func BreakDuration(b world.Block, i item.Stack) time.Duration { diff --git a/server/player/player.go b/server/player/player.go index 493c02938..4f9ee28df 100644 --- a/server/player/player.go +++ b/server/player/player.go @@ -1065,11 +1065,11 @@ func (p *Player) StopFlying() { func (p *Player) Sleep(pos cube.Pos) { w := p.World() b, ok := w.Block(pos).(block.Bed) - if ok && b.Occupied { + if !ok || b.User != nil { // The player cannot sleep here. return } - b.Occupied = true + b.User = p w.SetBlock(pos, b, nil) w.SetSleepRequirement(time.Second * 5) @@ -1094,7 +1094,7 @@ func (p *Player) Wake() { pos := p.sleepPos.Load() if b, ok := w.Block(pos).(block.Bed); ok { - b.Occupied = false + b.User = p w.SetBlock(pos, b, nil) } } @@ -1847,6 +1847,9 @@ func (p *Player) BreakBlock(pos cube.Pos) { w.SetBlock(pos, nil, nil) w.AddParticle(pos.Vec3Centre(), particle.BlockBreak{Block: b}) + if j, ok := b.(block.PostBreakable); ok { + j.PostBreak(pos, w, p) + } if breakable, ok := b.(block.Breakable); ok && !p.GameMode().CreativeInventory() { info := breakable.BreakInfo() if diff := (info.XPDrops[1] - info.XPDrops[0]) + 1; diff > 0 { diff --git a/server/session/entity_metadata.go b/server/session/entity_metadata.go index ab17b8a94..eb32569a9 100644 --- a/server/session/entity_metadata.go +++ b/server/session/entity_metadata.go @@ -24,6 +24,8 @@ func (s *Session) parseEntityMetadata(e world.Entity) entityMetadata { dataKeyPotionColour: int32(0), dataKeyPotionAmbient: byte(0), dataKeyColour: byte(0), + dataKeyFlags: int64(0), + dataKeyPlayerFlags: byte(0), } m.setFlag(dataKeyFlags, dataFlagAffectedByGravity) @@ -115,19 +117,12 @@ func (s *Session) parseEntityMetadata(e world.Entity) entityMetadata { // setFlag sets a flag with a specific index in the int64 stored in the entity metadata map to the value // passed. It is typically used for entity metadata flags. func (m entityMetadata) setFlag(key uint32, index uint8) { - v, ok := m[key] - if key == dataKeyPlayerFlags { - if ok { - m[key] = v.(byte) ^ 1< Date: Fri, 29 Jul 2022 20:47:57 -0700 Subject: [PATCH 25/41] fix post merge issues --- server/block/bed.go | 5 +---- server/block/explosion.go | 2 +- server/entity/item.go | 1 - 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/server/block/bed.go b/server/block/bed.go index 991a2d496..d8ab199eb 100644 --- a/server/block/bed.go +++ b/server/block/bed.go @@ -80,12 +80,9 @@ func (b Bed) Activate(pos cube.Pos, _ cube.Face, w *world.World, u item.User, _ if w.Dimension() != world.Overworld { w.SetBlock(pos, nil, nil) ExplosionConfig{ - World: w, - Pos: pos.Vec3Centre(), - Size: 5, SpawnFire: true, - }.Do() + }.Explode(w, pos.Vec3Centre()) return true } diff --git a/server/block/explosion.go b/server/block/explosion.go index ecbb8e29a..486d13752 100644 --- a/server/block/explosion.go +++ b/server/block/explosion.go @@ -45,7 +45,7 @@ type ExplodableEntity interface { // Explodable represents a block that can be exploded. type Explodable interface { // Explode is called when an explosion occurs. The block can react to the explosion using the configuration passed. - Explode(pos cube.Pos, c ExplosionConfig) + Explode(explosionPos mgl64.Vec3, pos cube.Pos, w *world.World, c ExplosionConfig) } // rays ... diff --git a/server/entity/item.go b/server/entity/item.go index b61af21df..33072912d 100644 --- a/server/entity/item.go +++ b/server/entity/item.go @@ -2,7 +2,6 @@ package entity import ( "fmt" - "github.com/df-mc/dragonfly/server/block" "github.com/df-mc/dragonfly/server/block/cube" "github.com/df-mc/dragonfly/server/internal/nbtconv" "github.com/df-mc/dragonfly/server/item" From 2833785dcd1d1d9778d7b8a224fcc304339c05bd Mon Sep 17 00:00:00 2001 From: DaPigGuy Date: Fri, 29 Jul 2022 20:51:40 -0700 Subject: [PATCH 26/41] fix post merge issues pt II --- server/entity/item.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/server/entity/item.go b/server/entity/item.go index 33072912d..d184abfb3 100644 --- a/server/entity/item.go +++ b/server/entity/item.go @@ -2,6 +2,7 @@ package entity import ( "fmt" + "github.com/df-mc/dragonfly/server/block" "github.com/df-mc/dragonfly/server/block/cube" "github.com/df-mc/dragonfly/server/internal/nbtconv" "github.com/df-mc/dragonfly/server/item" @@ -166,6 +167,19 @@ func (it *Item) collect(w *world.World, collector Collector, pos mgl64.Vec3) { _ = it.Close() } +// New creates and returns an Item with the item.Stack, position, and velocity provided. It doesn't spawn the Item +// by itself. +func (it *Item) New(stack item.Stack, pos, vel mgl64.Vec3) world.Entity { + itemEntity := NewItem(stack, pos) + itemEntity.vel = vel + return itemEntity +} + +// Explode ... +func (it *Item) Explode(mgl64.Vec3, float64, block.ExplosionConfig) { + _ = it.Close() +} + // DecodeNBT decodes the properties in a map to an Item and returns a new Item entity. func (it *Item) DecodeNBT(data map[string]any) any { i := nbtconv.MapItem(data, "Item") From 9267c86f0a2636d7bf20ab322adee9f2fce1db39 Mon Sep 17 00:00:00 2001 From: Tal Date: Fri, 29 Jul 2022 23:36:48 -0500 Subject: [PATCH 27/41] Update server/block/bed.go Co-authored-by: DaPigGuy --- server/block/bed.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/block/bed.go b/server/block/bed.go index d8ab199eb..26d854bb9 100644 --- a/server/block/bed.go +++ b/server/block/bed.go @@ -32,7 +32,7 @@ func (Bed) Model() world.BlockModel { // BreakInfo ... func (b Bed) BreakInfo() BreakInfo { - return newBreakInfo(0.2, alwaysHarvestable, axeEffective, oneOf(b)) + return newBreakInfo(0.2, alwaysHarvestable, nothingEffective, oneOf(b)) } // UseOnBlock ... From ed4d6aa35cf7cd7276df7c3dd07c88e94acbf7a6 Mon Sep 17 00:00:00 2001 From: JustTal Date: Sun, 31 Jul 2022 01:09:17 -0500 Subject: [PATCH 28/41] block/bed.go: Various fixes. --- server/block/bed.go | 40 ++++++++++++++++++++---------------- server/player/player.go | 18 ++++++++++++++++- server/session/text.go | 10 +++++++++ server/session/world.go | 8 ++++++++ server/world/sleep.go | 45 +++++++++++++++++++++++++---------------- server/world/world.go | 35 ++++++++++++++++++++++++++++++++ 6 files changed, 121 insertions(+), 35 deletions(-) diff --git a/server/block/bed.go b/server/block/bed.go index 26d854bb9..f2b4c5d05 100644 --- a/server/block/bed.go +++ b/server/block/bed.go @@ -7,7 +7,6 @@ import ( "github.com/df-mc/dragonfly/server/item" "github.com/df-mc/dragonfly/server/world" "github.com/go-gl/mathgl/mgl64" - "github.com/google/uuid" "github.com/sandertv/gophertunnel/minecraft/text" ) @@ -35,6 +34,17 @@ func (b Bed) BreakInfo() BreakInfo { return newBreakInfo(0.2, alwaysHarvestable, nothingEffective, oneOf(b)) } +// CanDisplace ... +func (Bed) CanDisplace(b world.Liquid) bool { + _, water := b.(Water) + return water +} + +// SideClosed ... +func (Bed) SideClosed(cube.Pos, cube.Pos, *world.World) bool { + return false +} + // UseOnBlock ... func (b Bed) UseOnBlock(pos cube.Pos, face cube.Face, _ mgl64.Vec3, w *world.World, user item.User, ctx *item.UseContext) (used bool) { if pos, _, used = firstReplaceable(w, pos, face, b); !used { @@ -56,23 +66,15 @@ func (b Bed) UseOnBlock(pos cube.Pos, face cube.Face, _ mgl64.Vec3, w *world.Wor return } + ctx.IgnoreBBox = true place(w, sidePos, side, user, ctx) place(w, pos, b, user, ctx) return placed(ctx) } -// sleeper represents an entity that can sleep on beds. -type sleeper interface { - item.User - UUID() uuid.UUID - Message(a ...any) - Sleep(pos cube.Pos) - Wake() -} - // Activate ... func (b Bed) Activate(pos cube.Pos, _ cube.Face, w *world.World, u item.User, _ *item.UseContext) bool { - s, ok := u.(sleeper) + s, ok := u.(world.Sleeper) if !ok { return false } @@ -93,7 +95,7 @@ func (b Bed) Activate(pos cube.Pos, _ cube.Face, w *world.World, u item.User, _ userPos := s.Position() if sidePos.Vec3Middle().Sub(userPos).Len() > 4 && pos.Vec3Middle().Sub(userPos).Len() > 4 { - s.Message(text.Colourf("Bed is too far away")) + s.Messaget(text.Colourf("%s", "%tile.bed.tooFar")) return true } @@ -101,17 +103,21 @@ func (b Bed) Activate(pos cube.Pos, _ cube.Face, w *world.World, u item.User, _ if !ok { return false } + if _, ok = w.Liquid(headPos); ok { + return false + } w.SetPlayerSpawn(s.UUID(), headPos) - s.Message(text.Colourf("Respawn point set")) time := w.Time() % world.TimeFull - if !(time >= world.TimeNight && time < world.TimeSunrise) { - s.Message(text.Colourf("You can only sleep at night")) + if (time < world.TimeNight || time >= world.TimeSunrise) && !w.ThunderingAt(pos) { + s.Messaget(text.Colourf("%s", "%tile.bed.respawnSet")) + s.Messaget(text.Colourf("%s", "%tile.bed.noSleep")) return true } if headSide.User != nil { - s.Message(text.Colourf("This bed is occupied")) + s.Messaget(text.Colourf("%s", "%tile.bed.respawnSet")) + s.Messaget(text.Colourf("%s", "%tile.bed.occupied")) return true } @@ -125,7 +131,7 @@ func (b Bed) PostBreak(pos cube.Pos, w *world.World, _ item.User) { if !ok { return } - if s, ok := headSide.User.(sleeper); ok { + if s, ok := headSide.User.(world.Sleeper); ok { s.Wake() } } diff --git a/server/player/player.go b/server/player/player.go index 36f47624e..d5955fe69 100644 --- a/server/player/player.go +++ b/server/player/player.go @@ -243,6 +243,11 @@ func (p *Player) Messagef(f string, a ...any) { p.session().SendMessage(fmt.Sprintf(f, a...)) } +// Messaget sends a message translation to the player. The message is translated client-side using the client's locale. +func (p *Player) Messaget(key string, a ...string) { + p.session().SendTranslation(key, a...) +} + // SendPopup sends a formatted popup to the player. The popup is shown above the hotbar of the player and // overwrites/is overwritten by the name of the item equipped. // The popup is formatted following the rules of fmt.Sprintln without a newline at the end. @@ -615,6 +620,7 @@ func (p *Player) Hurt(dmg float64, source damage.Source) (float64, bool) { if _, ok := source.(damage.SourceDrowning); ok { w.PlaySound(pos, sound.Drowning{}) } + p.Wake() p.immunity.Store(time.Now().Add(immunity)) if p.Dead() { @@ -1070,9 +1076,13 @@ func (p *Player) Sleep(pos cube.Pos) { b.User = p w.SetBlock(pos, b, nil) w.SetSleepRequirement(time.Second * 5) + w.BroadcastSleepingReminder(p) + p.pos.Store(pos.Vec3Middle().Add(mgl64.Vec3{0, 0.5625})) p.sleeping.Store(true) p.sleepPos.Store(pos) + + w.BroadcastSleepingIndicator() p.updateState() } @@ -1084,6 +1094,7 @@ func (p *Player) Wake() { w := p.World() w.SetSleepRequirement(0) + w.BroadcastSleepingIndicator() for _, v := range p.viewers() { v.ViewEntityWake(p) @@ -1092,7 +1103,7 @@ func (p *Player) Wake() { pos := p.sleepPos.Load() if b, ok := w.Block(pos).(block.Bed); ok { - b.User = p + b.User = nil w.SetBlock(pos, b, nil) } } @@ -1106,6 +1117,11 @@ func (p *Player) Sleeping() (cube.Pos, bool) { return p.sleepPos.Load(), true } +// SendSleepingIndicator displays a notification to the player on the amount of sleeping players in the world. +func (p *Player) SendSleepingIndicator(sleeping, max int) { + p.session().ViewSleepingPlayers(sleeping, max) +} + // Jump makes the player jump if they are on ground. It exhausts the player by 0.05 food points, an additional 0.15 // is exhausted if the player is sprint jumping. func (p *Player) Jump() { diff --git a/server/session/text.go b/server/session/text.go index 935d81e8e..4c885faa7 100644 --- a/server/session/text.go +++ b/server/session/text.go @@ -47,6 +47,16 @@ func (s *Session) SendJukeboxPopup(message string) { }) } +// SendTranslation ... +func (s *Session) SendTranslation(key string, a ...string) { + s.writePacket(&packet.Text{ + TextType: packet.TextTypeTranslation, + NeedsTranslation: true, + Message: key, + Parameters: a, + }) +} + // SendToast ... func (s *Session) SendToast(title, message string) { s.writePacket(&packet.ToastRequest{ diff --git a/server/session/world.go b/server/session/world.go index 9a647098f..30608fa30 100644 --- a/server/session/world.go +++ b/server/session/world.go @@ -294,6 +294,14 @@ func (s *Session) ViewItemCooldown(item world.Item, duration time.Duration) { }) } +// ViewSleepingPlayers ... +func (s *Session) ViewSleepingPlayers(sleeping, max int) { + s.writePacket(&packet.LevelEvent{ + EventType: packet.LevelEventSleepingPlayers, + EventData: int32((max << 16) | sleeping), + }) +} + // ViewParticle ... func (s *Session) ViewParticle(pos mgl64.Vec3, p world.Particle) { switch pa := p.(type) { diff --git a/server/world/sleep.go b/server/world/sleep.go index 8bc06fb34..386ed3e74 100644 --- a/server/world/sleep.go +++ b/server/world/sleep.go @@ -2,10 +2,20 @@ package world import ( "github.com/df-mc/dragonfly/server/block/cube" + "github.com/google/uuid" ) -// sleeper represents an entity that can sleep. -type sleeper interface { +// Sleeper represents an entity that can sleep. +type Sleeper interface { + Entity + + UUID() uuid.UUID + + Message(a ...any) + Messaget(key string, a ...string) + SendSleepingIndicator(sleeping, max int) + + Sleep(pos cube.Pos) Sleeping() (cube.Pos, bool) Wake() } @@ -13,20 +23,17 @@ type sleeper interface { // tryAdvanceDay attempts to advance the day of the world, by first ensuring that all sleepers are sleeping, and then // updating the time of day. func (t ticker) tryAdvanceDay() { - ent := t.w.Entities() - sleepers := make([]sleeper, 0, len(ent)) - for _, e := range ent { - if s, ok := e.(sleeper); ok { - sleepers = append(sleepers, s) - } - } + sleepers := t.w.Sleepers() if len(sleepers) == 0 { // No sleepers in the world. return } - advanceTime := true + thunderAnywhere, advanceTime := false, true for _, s := range sleepers { + if !thunderAnywhere { + thunderAnywhere = t.w.ThunderingAt(cube.PosFromVec3(s.Position())) + } if _, ok := s.Sleeping(); !ok { advanceTime = false break @@ -37,13 +44,17 @@ func (t ticker) tryAdvanceDay() { return } + for _, s := range sleepers { + s.Wake() + } + totalTime := t.w.Time() - timeOfDay := totalTime % TimeFull - if timeOfDay >= TimeNight && timeOfDay < TimeSunrise { - t.w.SetTime(totalTime + TimeFull - timeOfDay) - t.w.StopRaining() - for _, s := range sleepers { - s.Wake() - } + time := totalTime % TimeFull + if (time < TimeNight || time >= TimeSunrise) && !thunderAnywhere { + // The conditions for sleeping aren't being met. + return } + + t.w.SetTime(totalTime + TimeFull - time) + t.w.StopRaining() } diff --git a/server/world/world.go b/server/world/world.go index e2e4db5a0..646539585 100644 --- a/server/world/world.go +++ b/server/world/world.go @@ -789,6 +789,41 @@ func (w *World) Entities() []Entity { return m } +// Sleepers returns a list of all sleeping entities currently added to the World. +func (w *World) Sleepers() []Sleeper { + ent := w.Entities() + sleepers := make([]Sleeper, 0, len(ent)) + for _, e := range ent { + if s, ok := e.(Sleeper); ok { + sleepers = append(sleepers, s) + } + } + return sleepers +} + +// BroadcastSleepingIndicator broadcasts a sleeping indicator to all sleepers in the world. +func (w *World) BroadcastSleepingIndicator() { + sleepers := w.Sleepers() + sleeping := len(sliceutil.Filter(sleepers, func(s Sleeper) bool { + _, ok := s.Sleeping() + return ok + })) + for _, s := range sleepers { + s.SendSleepingIndicator(sleeping, len(sleepers)) + } +} + +// BroadcastSleepingReminder broadcasts a sleeping reminder message to all sleepers in the world, excluding the sleeper +// passed. +func (w *World) BroadcastSleepingReminder(sleeper Sleeper) { + for _, s := range w.Sleepers() { + if s == sleeper { + continue + } + s.Messaget("chat.type.sleeping", sleeper.Name()) + } +} + // OfEntity attempts to return a world that an entity is currently in. If the entity was not currently added // to a world, the world returned is nil and the bool returned is false. func OfEntity(e Entity) (*World, bool) { From fda1e53340933b329cf11e23f0ab49ee1a098fcb Mon Sep 17 00:00:00 2001 From: JustTal Date: Sun, 31 Jul 2022 01:25:56 -0500 Subject: [PATCH 29/41] player/handler.go: Added HandleSleepReminder. --- server/player/handler.go | 4 ++++ server/player/player.go | 24 +++++++++++++++--------- server/world/world.go | 6 +++--- 3 files changed, 22 insertions(+), 12 deletions(-) diff --git a/server/player/handler.go b/server/player/handler.go index 4ae54a021..5aafe7902 100644 --- a/server/player/handler.go +++ b/server/player/handler.go @@ -110,6 +110,9 @@ type Handler interface { // HandleSignEdit handles the player editing a sign. It is called for every keystroke while editing a sign and // has both the old text passed and the text after the edit. This typically only has a change of one character. HandleSignEdit(ctx *event.Context, oldText, newText string) + // HandleSleepReminder handles the player broadcasting a sleeping reminder. It is called in vanilla to remind other + // players to sleep in order to advance the day. + HandleSleepReminder(ctx *event.Context) // HandleItemDamage handles the event wherein the item either held by the player or as armour takes // damage through usage. // The type of the item may be checked to determine whether it was armour or a tool used. The damage to @@ -157,6 +160,7 @@ func (NopHandler) HandleBlockBreak(*event.Context, cube.Pos, *[]item.Stack) func (NopHandler) HandleBlockPlace(*event.Context, cube.Pos, world.Block) {} func (NopHandler) HandleBlockPick(*event.Context, cube.Pos, world.Block) {} func (NopHandler) HandleSignEdit(*event.Context, string, string) {} +func (NopHandler) HandleSleepReminder(*event.Context) {} func (NopHandler) HandleItemPickup(*event.Context, item.Stack) {} func (NopHandler) HandleItemUse(*event.Context) {} func (NopHandler) HandleItemUseOnBlock(*event.Context, cube.Pos, cube.Face, mgl64.Vec3) {} diff --git a/server/player/player.go b/server/player/player.go index d5955fe69..aa6ad1e69 100644 --- a/server/player/player.go +++ b/server/player/player.go @@ -1068,15 +1068,21 @@ func (p *Player) StopFlying() { // the player will not sleep. func (p *Player) Sleep(pos cube.Pos) { w := p.World() - b, ok := w.Block(pos).(block.Bed) - if !ok || b.User != nil { - // The player cannot sleep here. - return + if b, ok := w.Block(pos).(block.Bed); ok { + if b.User != nil { + // The player cannot sleep here. + return + } + b.User = p + w.SetBlock(pos, b, nil) + } + + w.SetRequiredSleepDuration(time.Second * 5) + + ctx := event.C() + if p.Handler().HandleSleepReminder(ctx); !ctx.Cancelled() { + w.BroadcastSleepingReminder(p) } - b.User = p - w.SetBlock(pos, b, nil) - w.SetSleepRequirement(time.Second * 5) - w.BroadcastSleepingReminder(p) p.pos.Store(pos.Vec3Middle().Add(mgl64.Vec3{0, 0.5625})) p.sleeping.Store(true) @@ -1093,7 +1099,7 @@ func (p *Player) Wake() { } w := p.World() - w.SetSleepRequirement(0) + w.SetRequiredSleepDuration(0) w.BroadcastSleepingIndicator() for _, v := range p.viewers() { diff --git a/server/world/world.go b/server/world/world.go index 646539585..9266758ae 100644 --- a/server/world/world.go +++ b/server/world/world.go @@ -891,9 +891,9 @@ func (w *World) SetPlayerSpawn(uuid uuid.UUID, pos cube.Pos) { } } -// SetSleepRequirement sets the duration of time players in the world must sleep for, in order for the time to change to -// day. -func (w *World) SetSleepRequirement(duration time.Duration) { +// SetRequiredSleepDuration sets the duration of time players in the world must sleep for, in order to advance to the +// next day. +func (w *World) SetRequiredSleepDuration(duration time.Duration) { if w == nil { return } From 9fa633db045bbfd5aa7b965ef998f81303dd4691 Mon Sep 17 00:00:00 2001 From: JustTal Date: Sun, 31 Jul 2022 01:30:04 -0500 Subject: [PATCH 30/41] block/break_info.go: Removed PostBreakable in favour of `withBreakHandler`. --- server/block/bed.go | 21 +++++++++------------ server/block/break_info.go | 14 ++++++++------ server/player/player.go | 10 +++++----- 3 files changed, 22 insertions(+), 23 deletions(-) diff --git a/server/block/bed.go b/server/block/bed.go index f2b4c5d05..a3ca9d273 100644 --- a/server/block/bed.go +++ b/server/block/bed.go @@ -31,7 +31,15 @@ func (Bed) Model() world.BlockModel { // BreakInfo ... func (b Bed) BreakInfo() BreakInfo { - return newBreakInfo(0.2, alwaysHarvestable, nothingEffective, oneOf(b)) + return newBreakInfo(0.2, alwaysHarvestable, nothingEffective, oneOf(b)).withBreakHandler(func(pos cube.Pos, w *world.World, u item.User) { + headSide, _, ok := b.head(pos, w) + if ok { + return + } + if s, ok := headSide.User.(world.Sleeper); ok { + s.Wake() + } + }) } // CanDisplace ... @@ -125,17 +133,6 @@ func (b Bed) Activate(pos cube.Pos, _ cube.Face, w *world.World, u item.User, _ return true } -// PostBreak ... -func (b Bed) PostBreak(pos cube.Pos, w *world.World, _ item.User) { - headSide, _, ok := b.head(pos, w) - if !ok { - return - } - if s, ok := headSide.User.(world.Sleeper); ok { - s.Wake() - } -} - // EntityLand ... func (b Bed) EntityLand(_ cube.Pos, _ *world.World, e world.Entity) { if s, ok := e.(sneakingEntity); ok && s.Sneaking() { diff --git a/server/block/break_info.go b/server/block/break_info.go index 06953e54d..b1bd182cb 100644 --- a/server/block/break_info.go +++ b/server/block/break_info.go @@ -16,12 +16,6 @@ type Breakable interface { BreakInfo() BreakInfo } -// PostBreakable represents a block that has an action(s) processed after the block is broken. -type PostBreakable interface { - // PostBreak is called after the block has broken. - PostBreak(pos cube.Pos, w *world.World, u item.User) -} - // BreakDuration returns the base duration that breaking the block passed takes when being broken using the // item passed. func BreakDuration(b world.Block, i item.Stack) time.Duration { @@ -90,6 +84,8 @@ type BreakInfo struct { Effective func(t item.Tool) bool // Drops is a function called to get the drops of the block if it is broken using the item passed. Drops func(t item.Tool, enchantments []item.Enchantment) []item.Stack + // BreakHandler is called after the block has broken. + BreakHandler func(pos cube.Pos, w *world.World, u item.User) // XPDrops is the range of XP a block can drop when broken. XPDrops XPDropRange // BlastResistance is the blast resistance of the block, which influences the block's ability to withstand an @@ -121,6 +117,12 @@ func (b BreakInfo) withBlastResistance(res float64) BreakInfo { return b } +// withBreakHandler sets the BreakHandler field of the BreakInfo struct to the passed value. +func (b BreakInfo) withBreakHandler(handler func(pos cube.Pos, w *world.World, u item.User)) BreakInfo { + b.BreakHandler = handler + return b +} + // XPDropRange holds the min & max XP drop amounts of blocks. type XPDropRange [2]int diff --git a/server/player/player.go b/server/player/player.go index aa6ad1e69..b092cabae 100644 --- a/server/player/player.go +++ b/server/player/player.go @@ -1854,12 +1854,12 @@ func (p *Player) BreakBlock(pos cube.Pos) { w.SetBlock(pos, nil, nil) w.AddParticle(pos.Vec3Centre(), particle.BlockBreak{Block: b}) - if j, ok := b.(block.PostBreakable); ok { - j.PostBreak(pos, w, p) - } - if breakable, ok := b.(block.Breakable); ok && !p.GameMode().CreativeInventory() { + if breakable, ok := b.(block.Breakable); ok { info := breakable.BreakInfo() - if diff := (info.XPDrops[1] - info.XPDrops[0]) + 1; diff > 0 { + if info.BreakHandler != nil { + info.BreakHandler(pos, w, p) + } + if diff := (info.XPDrops[1] - info.XPDrops[0]) + 1; diff > 0 && !p.GameMode().CreativeInventory() { amount := rand.Intn(diff) + info.XPDrops[0] for _, orb := range entity.NewExperienceOrbs(pos.Vec3Centre(), amount) { orb.SetVelocity(mgl64.Vec3{(rand.Float64()*0.2 - 0.1) * 2, rand.Float64() * 0.4, (rand.Float64()*0.2 - 0.1) * 2}) From 742e8f64b1ffff5a8ef20ac3e4e90dadcafea73f Mon Sep 17 00:00:00 2001 From: JustTal Date: Sun, 31 Jul 2022 01:31:46 -0500 Subject: [PATCH 31/41] block/bed.go: Moved to own function. --- server/block/bed.go | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/server/block/bed.go b/server/block/bed.go index a3ca9d273..15d1b3fcb 100644 --- a/server/block/bed.go +++ b/server/block/bed.go @@ -31,15 +31,7 @@ func (Bed) Model() world.BlockModel { // BreakInfo ... func (b Bed) BreakInfo() BreakInfo { - return newBreakInfo(0.2, alwaysHarvestable, nothingEffective, oneOf(b)).withBreakHandler(func(pos cube.Pos, w *world.World, u item.User) { - headSide, _, ok := b.head(pos, w) - if ok { - return - } - if s, ok := headSide.User.(world.Sleeper); ok { - s.Wake() - } - }) + return newBreakInfo(0.2, alwaysHarvestable, nothingEffective, oneOf(b)).withBreakHandler(b.handleBreak) } // CanDisplace ... @@ -149,6 +141,17 @@ func (b Bed) EntityLand(_ cube.Pos, _ *world.World, e world.Entity) { } } +// handleBreak handles the breaking of the bed. +func (b Bed) handleBreak(pos cube.Pos, w *world.World, _ item.User) { + headSide, _, ok := b.head(pos, w) + if ok { + return + } + if s, ok := headSide.User.(world.Sleeper); ok { + s.Wake() + } +} + // sneakingEntity represents an entity that can sneak. type sneakingEntity interface { // Sneaking returns true if the entity is currently sneaking. From 0b80371dfca3def445b29416249be95ace7f314d Mon Sep 17 00:00:00 2001 From: JustTal Date: Sun, 31 Jul 2022 01:33:33 -0500 Subject: [PATCH 32/41] block/bed.go: Implement MaxCounter. --- server/block/bed.go | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/server/block/bed.go b/server/block/bed.go index 15d1b3fcb..c473febe3 100644 --- a/server/block/bed.go +++ b/server/block/bed.go @@ -24,6 +24,11 @@ type Bed struct { User item.User } +// MaxCount always returns 1. +func (Bed) MaxCount() int { + return 1 +} + // Model ... func (Bed) Model() world.BlockModel { return model.Bed{} @@ -31,7 +36,15 @@ func (Bed) Model() world.BlockModel { // BreakInfo ... func (b Bed) BreakInfo() BreakInfo { - return newBreakInfo(0.2, alwaysHarvestable, nothingEffective, oneOf(b)).withBreakHandler(b.handleBreak) + return newBreakInfo(0.2, alwaysHarvestable, nothingEffective, oneOf(b)).withBreakHandler(func(pos cube.Pos, w *world.World, _ item.User) { + headSide, _, ok := b.head(pos, w) + if ok { + return + } + if s, ok := headSide.User.(world.Sleeper); ok { + s.Wake() + } + }) } // CanDisplace ... @@ -141,17 +154,6 @@ func (b Bed) EntityLand(_ cube.Pos, _ *world.World, e world.Entity) { } } -// handleBreak handles the breaking of the bed. -func (b Bed) handleBreak(pos cube.Pos, w *world.World, _ item.User) { - headSide, _, ok := b.head(pos, w) - if ok { - return - } - if s, ok := headSide.User.(world.Sleeper); ok { - s.Wake() - } -} - // sneakingEntity represents an entity that can sneak. type sneakingEntity interface { // Sneaking returns true if the entity is currently sneaking. From 4370276cffe1594824f0008d6ed1c90457acda25 Mon Sep 17 00:00:00 2001 From: JustTal Date: Sun, 31 Jul 2022 15:53:22 -0500 Subject: [PATCH 33/41] player/player.go: Potentially fix beds? --- server/player/player.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/server/player/player.go b/server/player/player.go index b092cabae..f84a26aec 100644 --- a/server/player/player.go +++ b/server/player/player.go @@ -1848,17 +1848,19 @@ func (p *Player) BreakBlock(pos cube.Pos) { p.resendBlocks(pos, w) return } + if breakable, ok := b.(block.Breakable); ok { + if info := breakable.BreakInfo(); info.BreakHandler != nil { + info.BreakHandler(pos, w, p) + } + } + held, left := p.HeldItems() p.SwingArm() w.SetBlock(pos, nil, nil) w.AddParticle(pos.Vec3Centre(), particle.BlockBreak{Block: b}) - if breakable, ok := b.(block.Breakable); ok { info := breakable.BreakInfo() - if info.BreakHandler != nil { - info.BreakHandler(pos, w, p) - } if diff := (info.XPDrops[1] - info.XPDrops[0]) + 1; diff > 0 && !p.GameMode().CreativeInventory() { amount := rand.Intn(diff) + info.XPDrops[0] for _, orb := range entity.NewExperienceOrbs(pos.Vec3Centre(), amount) { From cf846ef18446aaa0b0fad8795f9ef984ca47e735 Mon Sep 17 00:00:00 2001 From: JustTal Date: Sun, 31 Jul 2022 16:23:06 -0500 Subject: [PATCH 34/41] player/player.go: Actually fix beds. --- server/block/bed.go | 2 +- server/player/player.go | 8 +++----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/server/block/bed.go b/server/block/bed.go index c473febe3..8017769b0 100644 --- a/server/block/bed.go +++ b/server/block/bed.go @@ -38,7 +38,7 @@ func (Bed) Model() world.BlockModel { func (b Bed) BreakInfo() BreakInfo { return newBreakInfo(0.2, alwaysHarvestable, nothingEffective, oneOf(b)).withBreakHandler(func(pos cube.Pos, w *world.World, _ item.User) { headSide, _, ok := b.head(pos, w) - if ok { + if !ok { return } if s, ok := headSide.User.(world.Sleeper); ok { diff --git a/server/player/player.go b/server/player/player.go index f84a26aec..dcd2d8ba5 100644 --- a/server/player/player.go +++ b/server/player/player.go @@ -1848,11 +1848,6 @@ func (p *Player) BreakBlock(pos cube.Pos) { p.resendBlocks(pos, w) return } - if breakable, ok := b.(block.Breakable); ok { - if info := breakable.BreakInfo(); info.BreakHandler != nil { - info.BreakHandler(pos, w, p) - } - } held, left := p.HeldItems() @@ -1861,6 +1856,9 @@ func (p *Player) BreakBlock(pos cube.Pos) { w.AddParticle(pos.Vec3Centre(), particle.BlockBreak{Block: b}) if breakable, ok := b.(block.Breakable); ok { info := breakable.BreakInfo() + if info.BreakHandler != nil { + info.BreakHandler(pos, w, p) + } if diff := (info.XPDrops[1] - info.XPDrops[0]) + 1; diff > 0 && !p.GameMode().CreativeInventory() { amount := rand.Intn(diff) + info.XPDrops[0] for _, orb := range entity.NewExperienceOrbs(pos.Vec3Centre(), amount) { From d531be359b6a4081a1dd831c8eedad8809cfb3a2 Mon Sep 17 00:00:00 2001 From: Tal Date: Sun, 31 Jul 2022 22:55:08 -0500 Subject: [PATCH 35/41] Update server/block/bed.go Co-authored-by: DaPigGuy --- server/block/bed.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/block/bed.go b/server/block/bed.go index 8017769b0..873f956c8 100644 --- a/server/block/bed.go +++ b/server/block/bed.go @@ -18,7 +18,7 @@ type Bed struct { Colour item.Colour // Facing is the direction that the bed is facing. Facing cube.Direction - // Head is true if the bed is the head side or the bottom side. + // Head is true if the bed is the head side. Head bool // User is the user that is using the bed. User item.User From c20ff84c076007e78758a637413572eef5d91882 Mon Sep 17 00:00:00 2001 From: JustTal Date: Sun, 31 Jul 2022 22:58:29 -0500 Subject: [PATCH 36/41] block/bed.go: Use sourceWaterDisplacer. --- server/block/bed.go | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/server/block/bed.go b/server/block/bed.go index 873f956c8..8cd4bf3ec 100644 --- a/server/block/bed.go +++ b/server/block/bed.go @@ -13,6 +13,7 @@ import ( // Bed is a block, allowing players to sleep to set their spawns and skip the night. type Bed struct { transparent + sourceWaterDisplacer // Colour is the colour of the bed. Colour item.Colour @@ -34,6 +35,11 @@ func (Bed) Model() world.BlockModel { return model.Bed{} } +// SideClosed ... +func (Bed) SideClosed(cube.Pos, cube.Pos, *world.World) bool { + return false +} + // BreakInfo ... func (b Bed) BreakInfo() BreakInfo { return newBreakInfo(0.2, alwaysHarvestable, nothingEffective, oneOf(b)).withBreakHandler(func(pos cube.Pos, w *world.World, _ item.User) { @@ -47,17 +53,6 @@ func (b Bed) BreakInfo() BreakInfo { }) } -// CanDisplace ... -func (Bed) CanDisplace(b world.Liquid) bool { - _, water := b.(Water) - return water -} - -// SideClosed ... -func (Bed) SideClosed(cube.Pos, cube.Pos, *world.World) bool { - return false -} - // UseOnBlock ... func (b Bed) UseOnBlock(pos cube.Pos, face cube.Face, _ mgl64.Vec3, w *world.World, user item.User, ctx *item.UseContext) (used bool) { if pos, _, used = firstReplaceable(w, pos, face, b); !used { From 1162229b749ca323266a2a02b3ab6ee78708c1da Mon Sep 17 00:00:00 2001 From: DaPigGuy Date: Tue, 2 Aug 2022 17:32:09 -0700 Subject: [PATCH 37/41] fix formatting --- server/session/entity_metadata.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/session/entity_metadata.go b/server/session/entity_metadata.go index ffb0d1191..05bc493d5 100644 --- a/server/session/entity_metadata.go +++ b/server/session/entity_metadata.go @@ -162,8 +162,8 @@ const ( dataKeyExperienceValue = 15 dataKeyFireworkItem = 16 dataKeyCustomDisplay = 18 - dataKeyPlayerFlags = 26 - dataKeyBedPosition = 28 + dataKeyPlayerFlags = 26 + dataKeyBedPosition = 28 dataKeyPotionAuxValue = 36 dataKeyScale = 38 dataKeyMaxAir = 42 From 09abef4fcf68d1809cce2e99136cdd3903faf8ff Mon Sep 17 00:00:00 2001 From: JustTal Date: Fri, 19 Aug 2022 17:22:35 -0500 Subject: [PATCH 38/41] block/bed.go: Various improvements. --- server/block/bed.go | 18 +++++++++--------- server/block/farmland.go | 2 -- server/player/handler.go | 7 +++---- server/player/player.go | 16 +++++++--------- server/session/entity_metadata.go | 4 ++-- 5 files changed, 21 insertions(+), 26 deletions(-) diff --git a/server/block/bed.go b/server/block/bed.go index 8cd4bf3ec..f14fbb55d 100644 --- a/server/block/bed.go +++ b/server/block/bed.go @@ -21,7 +21,7 @@ type Bed struct { Facing cube.Direction // Head is true if the bed is the head side. Head bool - // User is the user that is using the bed. + // User is the user that is using the bed. It is only set for the Head part of the bed. User item.User } @@ -103,7 +103,7 @@ func (b Bed) Activate(pos cube.Pos, _ cube.Face, w *world.World, u item.User, _ userPos := s.Position() if sidePos.Vec3Middle().Sub(userPos).Len() > 4 && pos.Vec3Middle().Sub(userPos).Len() > 4 { - s.Messaget(text.Colourf("%s", "%tile.bed.tooFar")) + s.Messaget(text.Colourf("%%tile.bed.tooFar")) return true } @@ -119,13 +119,13 @@ func (b Bed) Activate(pos cube.Pos, _ cube.Face, w *world.World, u item.User, _ time := w.Time() % world.TimeFull if (time < world.TimeNight || time >= world.TimeSunrise) && !w.ThunderingAt(pos) { - s.Messaget(text.Colourf("%s", "%tile.bed.respawnSet")) - s.Messaget(text.Colourf("%s", "%tile.bed.noSleep")) + s.Messaget(text.Colourf("%%tile.bed.respawnSet")) + s.Messaget(text.Colourf("%%tile.bed.noSleep")) return true } if headSide.User != nil { - s.Messaget(text.Colourf("%s", "%tile.bed.respawnSet")) - s.Messaget(text.Colourf("%s", "%tile.bed.occupied")) + s.Messaget(text.Colourf("%%tile.bed.respawnSet")) + s.Messaget(text.Colourf("%%tile.bed.occupied")) return true } @@ -134,13 +134,13 @@ func (b Bed) Activate(pos cube.Pos, _ cube.Face, w *world.World, u item.User, _ } // EntityLand ... -func (b Bed) EntityLand(_ cube.Pos, _ *world.World, e world.Entity) { +func (b Bed) EntityLand(_ cube.Pos, _ *world.World, e world.Entity, distance *float64) { if s, ok := e.(sneakingEntity); ok && s.Sneaking() { // If the entity is sneaking, the fall distance and velocity stay the same. return } - if f, ok := e.(fallDistanceEntity); ok { - f.SetFallDistance(f.FallDistance() * 0.5) + if _, ok := e.(fallDistanceEntity); ok { + *distance *= 0.5 } if v, ok := e.(velocityEntity); ok { vel := v.Velocity() diff --git a/server/block/farmland.go b/server/block/farmland.go index b2391ed04..54c2027ce 100644 --- a/server/block/farmland.go +++ b/server/block/farmland.go @@ -81,8 +81,6 @@ func (f Farmland) EntityLand(pos cube.Pos, w *world.World, e world.Entity, dista type fallDistanceEntity interface { // ResetFallDistance resets the entities fall distance. ResetFallDistance() - // SetFallDistance sets the entities fall distance. - SetFallDistance(float64) // FallDistance returns the entities fall distance. FallDistance() float64 } diff --git a/server/player/handler.go b/server/player/handler.go index 5aafe7902..ad1f4e5d6 100644 --- a/server/player/handler.go +++ b/server/player/handler.go @@ -110,9 +110,8 @@ type Handler interface { // HandleSignEdit handles the player editing a sign. It is called for every keystroke while editing a sign and // has both the old text passed and the text after the edit. This typically only has a change of one character. HandleSignEdit(ctx *event.Context, oldText, newText string) - // HandleSleepReminder handles the player broadcasting a sleeping reminder. It is called in vanilla to remind other - // players to sleep in order to advance the day. - HandleSleepReminder(ctx *event.Context) + // HandleSleep handles the player going to sleep. ctx.Cancel() may be called to cancel the sleep. + HandleSleep(ctx *event.Context, sendReminder *bool) // HandleItemDamage handles the event wherein the item either held by the player or as armour takes // damage through usage. // The type of the item may be checked to determine whether it was armour or a tool used. The damage to @@ -160,7 +159,7 @@ func (NopHandler) HandleBlockBreak(*event.Context, cube.Pos, *[]item.Stack) func (NopHandler) HandleBlockPlace(*event.Context, cube.Pos, world.Block) {} func (NopHandler) HandleBlockPick(*event.Context, cube.Pos, world.Block) {} func (NopHandler) HandleSignEdit(*event.Context, string, string) {} -func (NopHandler) HandleSleepReminder(*event.Context) {} +func (NopHandler) HandleSleep(*event.Context, *bool) {} func (NopHandler) HandleItemPickup(*event.Context, item.Stack) {} func (NopHandler) HandleItemUse(*event.Context) {} func (NopHandler) HandleItemUseOnBlock(*event.Context, cube.Pos, cube.Face, mgl64.Vec3) {} diff --git a/server/player/player.go b/server/player/player.go index fea72ea43..ac437add0 100644 --- a/server/player/player.go +++ b/server/player/player.go @@ -275,12 +275,7 @@ func (p *Player) SendToast(title, message string) { // ResetFallDistance resets the player's fall distance. func (p *Player) ResetFallDistance() { - p.SetFallDistance(0) -} - -// SetFallDistance sets the player's fall distance. -func (p *Player) SetFallDistance(distance float64) { - p.fallDistance.Store(distance) + p.fallDistance.Store(0) } // FallDistance returns the player's fall distance. @@ -1067,6 +1062,11 @@ func (p *Player) StopFlying() { // Sleep makes the player sleep at the given position. If the position does not map to a bed (specifically the head side), // the player will not sleep. func (p *Player) Sleep(pos cube.Pos) { + ctx, sendReminder := event.C(), true + if p.Handler().HandleSleep(ctx, &sendReminder); ctx.Cancelled() { + return + } + w := p.World() if b, ok := w.Block(pos).(block.Bed); ok { if b.User != nil { @@ -1078,9 +1078,7 @@ func (p *Player) Sleep(pos cube.Pos) { } w.SetRequiredSleepDuration(time.Second * 5) - - ctx := event.C() - if p.Handler().HandleSleepReminder(ctx); !ctx.Cancelled() { + if sendReminder { w.BroadcastSleepingReminder(p) } diff --git a/server/session/entity_metadata.go b/server/session/entity_metadata.go index 05bc493d5..1e5899f99 100644 --- a/server/session/entity_metadata.go +++ b/server/session/entity_metadata.go @@ -92,7 +92,7 @@ func (s *Session) parseEntityMetadata(e world.Entity) entityMetadata { if sl, ok := e.(sleeper); ok { if pos, ok := sl.Sleeping(); ok { m[dataKeyBedPosition] = blockPosToProtocol(pos) - m.setFlag(dataKeyPlayerFlags, dataPlayerFlagSleep) + m.setFlag(dataKeyPlayerFlags, playerDataFlagSleep) } } if c, ok := e.(areaEffectCloud); ok { @@ -182,7 +182,7 @@ const ( ) const ( - dataPlayerFlagSleep = iota + 1 + playerDataFlagSleep = iota + 1 ) //noinspection GoUnusedConst From 9d31e874c02af667eda55f142dc8347fd290225f Mon Sep 17 00:00:00 2001 From: JustTal Date: Fri, 19 Aug 2022 17:27:24 -0500 Subject: [PATCH 39/41] world/world.go: Allocate a smaller slice of sleepers. --- server/world/world.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/world/world.go b/server/world/world.go index 9cc2ea0ec..e01c03b8a 100644 --- a/server/world/world.go +++ b/server/world/world.go @@ -792,7 +792,7 @@ func (w *World) Entities() []Entity { // Sleepers returns a list of all sleeping entities currently added to the World. func (w *World) Sleepers() []Sleeper { ent := w.Entities() - sleepers := make([]Sleeper, 0, len(ent)) + sleepers := make([]Sleeper, 0, len(ent)/40) for _, e := range ent { if s, ok := e.(Sleeper); ok { sleepers = append(sleepers, s) From 7c00325069393def3395c70f84ac0af63db989c6 Mon Sep 17 00:00:00 2001 From: DaPigGuy Date: Fri, 16 Dec 2022 09:50:58 -0800 Subject: [PATCH 40/41] fix merge issues --- server/block/bed.go | 4 ++-- server/player/type.go | 10 ++++++++-- server/session/entity_metadata.go | 4 ++-- server/world/sleep.go | 1 + 4 files changed, 13 insertions(+), 6 deletions(-) diff --git a/server/block/bed.go b/server/block/bed.go index f14fbb55d..ed03f9803 100644 --- a/server/block/bed.go +++ b/server/block/bed.go @@ -62,7 +62,7 @@ func (b Bed) UseOnBlock(pos cube.Pos, face cube.Face, _ mgl64.Vec3, w *world.Wor return } - b.Facing = user.Facing() + b.Facing = user.Rotation().Direction() side, sidePos := b, pos.Side(b.Facing.Face()) side.Head = true @@ -194,7 +194,7 @@ func (b Bed) EncodeNBT() map[string]interface{} { // DecodeNBT ... func (b Bed) DecodeNBT(data map[string]interface{}) interface{} { - b.Colour = item.Colours()[nbtconv.Map[uint8](data, "color")] + b.Colour = item.Colours()[nbtconv.Uint8(data, "color")] return b } diff --git a/server/player/type.go b/server/player/type.go index d76edb5c5..b548e3ac7 100644 --- a/server/player/type.go +++ b/server/player/type.go @@ -13,9 +13,15 @@ func (Type) NetworkOffset() float64 { return 1.62 } func (Type) BBox(e world.Entity) cube.BBox { p := e.(*Player) s := p.Scale() - switch { + // TODO: Shrink BBox for sneaking once implemented in Bedrock Edition. This is already a thing in Java Edition. - case p.Gliding(), p.Swimming(): + gliding := p.Gliding() + swimming := p.Swimming() + _, sleeping := p.Sleeping() + switch { + case sleeping: + return cube.Box(-0.1*s, 0, -0.1*s, 0.1*s, 0.2*s, 0.1*s) + case gliding, 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) diff --git a/server/session/entity_metadata.go b/server/session/entity_metadata.go index a5271b848..0ea4246c6 100644 --- a/server/session/entity_metadata.go +++ b/server/session/entity_metadata.go @@ -98,8 +98,8 @@ func (s *Session) parseEntityMetadata(e world.Entity) protocol.EntityMetadata { } if sl, ok := e.(sleeper); ok { if pos, ok := sl.Sleeping(); ok { - m[dataKeyBedPosition] = blockPosToProtocol(pos) - m.setFlag(dataKeyPlayerFlags, playerDataFlagSleep) + m[protocol.EntityDataKeyBedPosition] = blockPosToProtocol(pos) + m.SetFlag(protocol.EntityDataKeyPlayerFlags, protocol.EntityDataFlagSleeping) } } if c, ok := e.(areaEffectCloud); ok { diff --git a/server/world/sleep.go b/server/world/sleep.go index 386ed3e74..6d6bd8cbf 100644 --- a/server/world/sleep.go +++ b/server/world/sleep.go @@ -10,6 +10,7 @@ type Sleeper interface { Entity UUID() uuid.UUID + Name() string Message(a ...any) Messaget(key string, a ...string) From 29b63a9805fdde22c172a5401ea76ba27e5f73c1 Mon Sep 17 00:00:00 2001 From: JustTal Date: Fri, 16 Dec 2022 13:58:32 -0600 Subject: [PATCH 41/41] world/sleep.go: Various improvements. --- server/world/sleep.go | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/server/world/sleep.go b/server/world/sleep.go index 6d6bd8cbf..3bdaaa601 100644 --- a/server/world/sleep.go +++ b/server/world/sleep.go @@ -30,20 +30,16 @@ func (t ticker) tryAdvanceDay() { return } - thunderAnywhere, advanceTime := false, true + var thunderAnywhere bool for _, s := range sleepers { if !thunderAnywhere { thunderAnywhere = t.w.ThunderingAt(cube.PosFromVec3(s.Position())) } if _, ok := s.Sleeping(); !ok { - advanceTime = false - break + // We can't advance the time - not everyone is sleeping. + return } } - if !advanceTime { - // We can't advance the time - not everyone is sleeping. - return - } for _, s := range sleepers { s.Wake()