From 115bf76187da9c9af59aa26f95ec9300bed3fd4f Mon Sep 17 00:00:00 2001 From: RoyalMCPE Date: Sun, 16 Jul 2023 21:29:40 -0600 Subject: [PATCH 1/7] Base animation footprint --- server/entity/animation/animation.go | 49 ++++++++++++++++++++++++++++ server/session/world.go | 9 +++-- server/world/viewer.go | 7 ++-- server/world/world.go | 17 +++++++++- 4 files changed, 75 insertions(+), 7 deletions(-) create mode 100644 server/entity/animation/animation.go diff --git a/server/entity/animation/animation.go b/server/entity/animation/animation.go new file mode 100644 index 000000000..91bae9a0e --- /dev/null +++ b/server/entity/animation/animation.go @@ -0,0 +1,49 @@ +package animation + +// Animation represents an animation & controller that may be attached to an entity. +// Animations and controllers must be defined in a resource pack +type Animation struct { + name, state, controller string + stopCondition string +} + +// New returns a new animation that can be attached to an entity. By default no controller or state is sent to the viewer. +// To add a state and controller use WithController and WithState respectively. +func New(animation string) Animation { + return Animation{ + name: animation, + state: "", + controller: "", + stopCondition: "", + } +} + +// Name returns the name of the animation to be played +func (a Animation) Name() string { + return a.name +} + +// WithController sets the controller with the specified state. +// The controller must be added in a resource pack +func (a Animation) WithController(controller string) Animation { + a.controller = controller + return a +} + +// Controller returns the name of the controller being used. Controller returns an empty string if +// no controller was previously set +func (a Animation) Controller() string { + return a.controller +} + +// WithState sets the state to transition to as defined in the controller. +func (a Animation) WithState(state string) Animation { + a.state = state + return a +} + +// State returns the current state being played. State returns an empty string if +// no controller was previously set +func (a Animation) State() string { + return a.state +} diff --git a/server/session/world.go b/server/session/world.go index 4624fb85d..ffecb01e4 100644 --- a/server/session/world.go +++ b/server/session/world.go @@ -1,12 +1,13 @@ package session import ( - "github.com/df-mc/dragonfly/server/entity/effect" "image/color" "math/rand" "strings" "time" + "github.com/df-mc/dragonfly/server/entity/effect" + "github.com/df-mc/dragonfly/server/block" "github.com/df-mc/dragonfly/server/block/cube" "github.com/df-mc/dragonfly/server/entity" @@ -915,9 +916,11 @@ func (s *Session) ViewEntityState(e world.Entity) { } // ViewEntityAnimation ... -func (s *Session) ViewEntityAnimation(e world.Entity, animationName string) { +func (s *Session) ViewEntityAnimation(e world.Entity, animationName, state, controller string) { s.writePacket(&packet.AnimateEntity{ - Animation: animationName, + Animation: animationName, + NextState: state, + Controller: controller, EntityRuntimeIDs: []uint64{ s.entityRuntimeID(e), }, diff --git a/server/world/viewer.go b/server/world/viewer.go index eae4c8562..a2cbd0c2b 100644 --- a/server/world/viewer.go +++ b/server/world/viewer.go @@ -1,11 +1,12 @@ package world import ( + "time" + "github.com/df-mc/dragonfly/server/block/cube" "github.com/df-mc/dragonfly/server/world/chunk" "github.com/go-gl/mathgl/mgl64" "github.com/google/uuid" - "time" ) // Viewer is a viewer in the world. It can view changes that are made in the world, such as the addition of @@ -48,7 +49,7 @@ type Viewer interface { // physical appearance, for example when sprinting. ViewEntityState(e Entity) // ViewEntityAnimation starts viewing an animation performed by an entity. The animation has to be from a resource pack. - ViewEntityAnimation(e Entity, animationName string) + ViewEntityAnimation(e Entity, animationName, nextState, controller string) // ViewParticle views a particle spawned at a given position in the world. It is called when a particle, // for example a block breaking particle, is spawned near the player. ViewParticle(pos mgl64.Vec3, p Particle) @@ -89,7 +90,7 @@ func (NopViewer) ViewEntityItems(Entity) {} func (NopViewer) ViewEntityArmour(Entity) {} func (NopViewer) ViewEntityAction(Entity, EntityAction) {} func (NopViewer) ViewEntityState(Entity) {} -func (NopViewer) ViewEntityAnimation(Entity, string) {} +func (NopViewer) ViewEntityAnimation(Entity, string, string, string) {} func (NopViewer) ViewParticle(mgl64.Vec3, Particle) {} func (NopViewer) ViewSound(mgl64.Vec3, Sound) {} func (NopViewer) ViewBlockUpdate(cube.Pos, Block, int) {} diff --git a/server/world/world.go b/server/world/world.go index f54151122..9dbec40f7 100644 --- a/server/world/world.go +++ b/server/world/world.go @@ -2,13 +2,15 @@ package world import ( "errors" - "github.com/df-mc/goleveldb/leveldb" "math/rand" "sync" "time" + "github.com/df-mc/goleveldb/leveldb" + "github.com/df-mc/atomic" "github.com/df-mc/dragonfly/server/block/cube" + "github.com/df-mc/dragonfly/server/entity/animation" "github.com/df-mc/dragonfly/server/event" "github.com/df-mc/dragonfly/server/internal/sliceutil" "github.com/df-mc/dragonfly/server/world/chunk" @@ -643,6 +645,19 @@ func (w *World) AddParticle(pos mgl64.Vec3, p Particle) { } } +// PlayAnimation will start an animation for the specified entity. Viewers that are viewing the entity will be +// played the animation. +func (w *World) PlayAnimation(e Entity, animation animation.Animation) { + // Ignore if no animation name has been given + if animation.Name() == "" { + return + } + + for _, v := range w.Viewers(e.Position()) { + v.ViewEntityAnimation(e, animation.Name(), animation.State(), animation.Controller()) + } +} + // PlaySound plays a sound at a specific position in the world. Viewers of that position will be able to hear // the sound if they're close enough. func (w *World) PlaySound(pos mgl64.Vec3, s Sound) { From 097e8670dca1a03d1f85181000152e57a4d64d2c Mon Sep 17 00:00:00 2001 From: RoyalMCPE Date: Sun, 16 Jul 2023 21:49:46 -0600 Subject: [PATCH 2/7] Add support for stop conditions --- server/entity/animation/animation.go | 9 +++++++++ server/session/world.go | 10 ++++++---- server/world/viewer.go | 4 ++-- server/world/world.go | 2 +- 4 files changed, 18 insertions(+), 7 deletions(-) diff --git a/server/entity/animation/animation.go b/server/entity/animation/animation.go index 91bae9a0e..8193d0db6 100644 --- a/server/entity/animation/animation.go +++ b/server/entity/animation/animation.go @@ -47,3 +47,12 @@ func (a Animation) WithState(state string) Animation { func (a Animation) State() string { return a.state } + +func (a Animation) WithStopCondition(condition string) Animation { + a.stopCondition = condition + return a +} + +func (a Animation) StopCondition() string { + return a.stopCondition +} diff --git a/server/session/world.go b/server/session/world.go index ffecb01e4..db68b8128 100644 --- a/server/session/world.go +++ b/server/session/world.go @@ -916,11 +916,13 @@ func (s *Session) ViewEntityState(e world.Entity) { } // ViewEntityAnimation ... -func (s *Session) ViewEntityAnimation(e world.Entity, animationName, state, controller string) { +func (s *Session) ViewEntityAnimation(e world.Entity, animationName, state, stopCondition, controller string, stopConditionVersion int32) { s.writePacket(&packet.AnimateEntity{ - Animation: animationName, - NextState: state, - Controller: controller, + Animation: animationName, + NextState: state, + StopCondition: stopCondition, + StopConditionVersion: stopConditionVersion, + Controller: controller, EntityRuntimeIDs: []uint64{ s.entityRuntimeID(e), }, diff --git a/server/world/viewer.go b/server/world/viewer.go index a2cbd0c2b..e48b6bee5 100644 --- a/server/world/viewer.go +++ b/server/world/viewer.go @@ -49,7 +49,7 @@ type Viewer interface { // physical appearance, for example when sprinting. ViewEntityState(e Entity) // ViewEntityAnimation starts viewing an animation performed by an entity. The animation has to be from a resource pack. - ViewEntityAnimation(e Entity, animationName, nextState, controller string) + ViewEntityAnimation(e Entity, animationName, nextState, stopCondition, controller string) // ViewParticle views a particle spawned at a given position in the world. It is called when a particle, // for example a block breaking particle, is spawned near the player. ViewParticle(pos mgl64.Vec3, p Particle) @@ -90,7 +90,7 @@ func (NopViewer) ViewEntityItems(Entity) {} func (NopViewer) ViewEntityArmour(Entity) {} func (NopViewer) ViewEntityAction(Entity, EntityAction) {} func (NopViewer) ViewEntityState(Entity) {} -func (NopViewer) ViewEntityAnimation(Entity, string, string, string) {} +func (NopViewer) ViewEntityAnimation(Entity, string, string, string, string) {} func (NopViewer) ViewParticle(mgl64.Vec3, Particle) {} func (NopViewer) ViewSound(mgl64.Vec3, Sound) {} func (NopViewer) ViewBlockUpdate(cube.Pos, Block, int) {} diff --git a/server/world/world.go b/server/world/world.go index 9dbec40f7..8f4a45655 100644 --- a/server/world/world.go +++ b/server/world/world.go @@ -654,7 +654,7 @@ func (w *World) PlayAnimation(e Entity, animation animation.Animation) { } for _, v := range w.Viewers(e.Position()) { - v.ViewEntityAnimation(e, animation.Name(), animation.State(), animation.Controller()) + v.ViewEntityAnimation(e, animation.Name(), animation.State(), animation.StopCondition(), animation.Controller()) } } From 08df7d8306a9774aaa66267235ca016498f32ac5 Mon Sep 17 00:00:00 2001 From: RoyalMCPE Date: Sun, 16 Jul 2023 21:52:58 -0600 Subject: [PATCH 3/7] Document stop condition methods --- server/entity/animation/animation.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/server/entity/animation/animation.go b/server/entity/animation/animation.go index 8193d0db6..7fc3e538c 100644 --- a/server/entity/animation/animation.go +++ b/server/entity/animation/animation.go @@ -48,11 +48,14 @@ func (a Animation) State() string { return a.state } +// WithStopCondition takes the molang expression and stops the animation if the query passes. func (a Animation) WithStopCondition(condition string) Animation { a.stopCondition = condition return a } +// StopCondition returns the stop condition. StopCondition returns an empty string if +// no molang expression was set func (a Animation) StopCondition() string { return a.stopCondition } From afe4f9f66d8686d352a932e16c3b282fd20a3da8 Mon Sep 17 00:00:00 2001 From: RoyalMCPE Date: Sun, 16 Jul 2023 21:58:31 -0600 Subject: [PATCH 4/7] viewer.go: Cleanup ViewEntityAnimation signature --- server/session/world.go | 13 +++++++------ server/world/viewer.go | 5 +++-- server/world/world.go | 2 +- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/server/session/world.go b/server/session/world.go index db68b8128..e088ead7a 100644 --- a/server/session/world.go +++ b/server/session/world.go @@ -6,6 +6,7 @@ import ( "strings" "time" + "github.com/df-mc/dragonfly/server/entity/animation" "github.com/df-mc/dragonfly/server/entity/effect" "github.com/df-mc/dragonfly/server/block" @@ -916,13 +917,13 @@ func (s *Session) ViewEntityState(e world.Entity) { } // ViewEntityAnimation ... -func (s *Session) ViewEntityAnimation(e world.Entity, animationName, state, stopCondition, controller string, stopConditionVersion int32) { +func (s *Session) ViewEntityAnimation(e world.Entity, animation animation.Animation) { s.writePacket(&packet.AnimateEntity{ - Animation: animationName, - NextState: state, - StopCondition: stopCondition, - StopConditionVersion: stopConditionVersion, - Controller: controller, + Animation: animation.Name(), + NextState: animation.State(), + StopCondition: animation.StopCondition(), + StopConditionVersion: 0, // It doesn't seem like this affects anything + Controller: animation.Controller(), EntityRuntimeIDs: []uint64{ s.entityRuntimeID(e), }, diff --git a/server/world/viewer.go b/server/world/viewer.go index e48b6bee5..f112577cb 100644 --- a/server/world/viewer.go +++ b/server/world/viewer.go @@ -4,6 +4,7 @@ import ( "time" "github.com/df-mc/dragonfly/server/block/cube" + "github.com/df-mc/dragonfly/server/entity/animation" "github.com/df-mc/dragonfly/server/world/chunk" "github.com/go-gl/mathgl/mgl64" "github.com/google/uuid" @@ -49,7 +50,7 @@ type Viewer interface { // physical appearance, for example when sprinting. ViewEntityState(e Entity) // ViewEntityAnimation starts viewing an animation performed by an entity. The animation has to be from a resource pack. - ViewEntityAnimation(e Entity, animationName, nextState, stopCondition, controller string) + ViewEntityAnimation(e Entity, animation animation.Animation) // ViewParticle views a particle spawned at a given position in the world. It is called when a particle, // for example a block breaking particle, is spawned near the player. ViewParticle(pos mgl64.Vec3, p Particle) @@ -90,7 +91,7 @@ func (NopViewer) ViewEntityItems(Entity) {} func (NopViewer) ViewEntityArmour(Entity) {} func (NopViewer) ViewEntityAction(Entity, EntityAction) {} func (NopViewer) ViewEntityState(Entity) {} -func (NopViewer) ViewEntityAnimation(Entity, string, string, string, string) {} +func (NopViewer) ViewEntityAnimation(Entity, animation.Animation) {} func (NopViewer) ViewParticle(mgl64.Vec3, Particle) {} func (NopViewer) ViewSound(mgl64.Vec3, Sound) {} func (NopViewer) ViewBlockUpdate(cube.Pos, Block, int) {} diff --git a/server/world/world.go b/server/world/world.go index 8f4a45655..ddf8d6403 100644 --- a/server/world/world.go +++ b/server/world/world.go @@ -654,7 +654,7 @@ func (w *World) PlayAnimation(e Entity, animation animation.Animation) { } for _, v := range w.Viewers(e.Position()) { - v.ViewEntityAnimation(e, animation.Name(), animation.State(), animation.StopCondition(), animation.Controller()) + v.ViewEntityAnimation(e, animation) } } From fb518b1ff169f4d72a2ef99cbcc12ad3846ec4e7 Mon Sep 17 00:00:00 2001 From: RoyalMCPE Date: Sun, 16 Jul 2023 22:06:29 -0600 Subject: [PATCH 5/7] session\world.go: Leave StopConditionVersion as it's default --- server/session/world.go | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/server/session/world.go b/server/session/world.go index e088ead7a..97249f5e3 100644 --- a/server/session/world.go +++ b/server/session/world.go @@ -919,11 +919,10 @@ func (s *Session) ViewEntityState(e world.Entity) { // ViewEntityAnimation ... func (s *Session) ViewEntityAnimation(e world.Entity, animation animation.Animation) { s.writePacket(&packet.AnimateEntity{ - Animation: animation.Name(), - NextState: animation.State(), - StopCondition: animation.StopCondition(), - StopConditionVersion: 0, // It doesn't seem like this affects anything - Controller: animation.Controller(), + Animation: animation.Name(), + NextState: animation.State(), + StopCondition: animation.StopCondition(), + Controller: animation.Controller(), EntityRuntimeIDs: []uint64{ s.entityRuntimeID(e), }, From 6c8fe33c60f9e436f5e4b7702cf3a78ff61f1c66 Mon Sep 17 00:00:00 2001 From: RoyalMCPE Date: Sun, 16 Jul 2023 23:08:53 -0600 Subject: [PATCH 6/7] animation.go: Rename variable to be more descriptive --- server/entity/animation/animation.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/entity/animation/animation.go b/server/entity/animation/animation.go index 7fc3e538c..56710ee68 100644 --- a/server/entity/animation/animation.go +++ b/server/entity/animation/animation.go @@ -9,9 +9,9 @@ type Animation struct { // New returns a new animation that can be attached to an entity. By default no controller or state is sent to the viewer. // To add a state and controller use WithController and WithState respectively. -func New(animation string) Animation { +func New(animationName string) Animation { return Animation{ - name: animation, + name: animationName, state: "", controller: "", stopCondition: "", From 06bedd9ce3e92d3d818174737c87aa31f7f1ef20 Mon Sep 17 00:00:00 2001 From: RoyalMCPE Date: Sun, 23 Jul 2023 12:26:06 -0600 Subject: [PATCH 7/7] world.go: Rename PlayAnimation -> AnimateEntity --- server/world/world.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/world/world.go b/server/world/world.go index ddf8d6403..b4b4eee65 100644 --- a/server/world/world.go +++ b/server/world/world.go @@ -645,9 +645,9 @@ func (w *World) AddParticle(pos mgl64.Vec3, p Particle) { } } -// PlayAnimation will start an animation for the specified entity. Viewers that are viewing the entity will be +// AnimateEntity will start an animation for the specified entity. Viewers that are viewing the entity will be // played the animation. -func (w *World) PlayAnimation(e Entity, animation animation.Animation) { +func (w *World) AnimateEntity(e Entity, animation animation.Animation) { // Ignore if no animation name has been given if animation.Name() == "" { return