From 6fbdf9cafa1ffedd8b2a03b885612a34a923d8ce Mon Sep 17 00:00:00 2001 From: TwistedAsylumMC Date: Mon, 18 Nov 2024 08:52:57 +0000 Subject: [PATCH 1/2] world/viewer.go: Allow for more complex entity animations to be played --- server/entity/animation/animation.go | 63 ++++++++++++++++++++++++++++ server/session/world.go | 8 +++- server/world/tx.go | 8 ++++ server/world/viewer.go | 7 ++-- 4 files changed, 81 insertions(+), 5 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..11bde71c1 --- /dev/null +++ b/server/entity/animation/animation.go @@ -0,0 +1,63 @@ +package animation + +// Animation represents an animation that may be played on an entity from an active resource pack on +// the client. +type Animation struct { + name string + nextState string + controller string + stopCondition string +} + +// New returns a new animation that can be played on an entity. If no controller or stop condition is set, +// the animation will play for its full duration, including looping. Controllers can be set to manage +// multiple states of animations. It is also possible to use vanilla animations/controllers if they work +// for your entity, i.e. "animation.pig.baby_transform". +func New(name string) Animation { + return Animation{name: name} +} + +// Name returns the name of the animation to be played. +func (a Animation) Name() string { + return a.name +} + +// Controller returns the name of the controller to be used for the animation. +func (a Animation) Controller() string { + return a.controller +} + +// WithController returns a copy of the Animation with the provided animation controller. An animation +// controller with the same name must be defined in a resource pack for it to work. +func (a Animation) WithController(controller string) Animation { + a.controller = controller + return a +} + +// NextState returns the state to transition to after the animation has finished playing within the +// animation controller. +func (a Animation) NextState() string { + return a.nextState +} + +// WithNextState returns a copy of the Animation with the provided state to transition to after the +// animation has finished playing within the animation controller. +func (a Animation) WithNextState(state string) Animation { + a.nextState = state + return a +} + +// StopCondition returns the condition that must be met for the animation to stop playing. This is often +// a Molang expression that can be used to query various entity properties to determine when the animation +// should stop playing. +func (a Animation) StopCondition() string { + return a.stopCondition +} + +// WithStopCondition returns a copy of the Animation with the provided stop condition. The stop condition +// is a Molang expression that can be used to query various entity properties to determine when the animation +// should stop playing. +func (a Animation) WithStopCondition(condition string) Animation { + a.stopCondition = condition + return a +} diff --git a/server/session/world.go b/server/session/world.go index 586294c56..2770567df 100644 --- a/server/session/world.go +++ b/server/session/world.go @@ -1,6 +1,7 @@ package session import ( + "github.com/df-mc/dragonfly/server/entity/animation" "github.com/df-mc/dragonfly/server/entity/effect" "image/color" "math/rand" @@ -976,9 +977,12 @@ func (s *Session) ViewEntityState(e world.Entity) { } // ViewEntityAnimation ... -func (s *Session) ViewEntityAnimation(e world.Entity, animationName string) { +func (s *Session) ViewEntityAnimation(e world.Entity, a animation.Animation) { s.writePacket(&packet.AnimateEntity{ - Animation: animationName, + Animation: a.Name(), + NextState: a.NextState(), + StopCondition: a.StopCondition(), + Controller: a.Controller(), EntityRuntimeIDs: []uint64{ s.entityRuntimeID(e), }, diff --git a/server/world/tx.go b/server/world/tx.go index d82ed12af..75944ef80 100644 --- a/server/world/tx.go +++ b/server/world/tx.go @@ -158,6 +158,14 @@ func (tx *Tx) AddParticle(pos mgl64.Vec3, p Particle) { tx.World().addParticle(pos, p) } +// PlayEntityAnimation plays an animation on an entity in the World. The animation is played for all viewers +// of the entity. +func (tx *Tx) PlayEntityAnimation(e Entity, a EntityAnimation) { + for _, viewer := range tx.World().viewersOf(e.Position()) { + viewer.ViewEntityAnimation(e, a) + } +} + // PlaySound plays a sound at a specific position in the World. Viewers of that // position will be able to hear the sound if they are close enough. func (tx *Tx) PlaySound(pos mgl64.Vec3, s Sound) { diff --git a/server/world/viewer.go b/server/world/viewer.go index eab45c57f..8d20cf007 100644 --- a/server/world/viewer.go +++ b/server/world/viewer.go @@ -2,6 +2,7 @@ package world import ( "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,8 +50,8 @@ type Viewer interface { // ViewEntityState views the current state of an Entity. It is called whenever an Entity changes its // 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 starts viewing an animation performed by an Entity. + ViewEntityAnimation(e Entity, a 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) @@ -91,7 +92,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, animation.Animation) {} func (NopViewer) ViewParticle(mgl64.Vec3, Particle) {} func (NopViewer) ViewSound(mgl64.Vec3, Sound) {} func (NopViewer) ViewBlockUpdate(cube.Pos, Block, int) {} From e1a462bc7097f63276aa5b4b1e5d2969122e3f46 Mon Sep 17 00:00:00 2001 From: TwistedAsylumMC Date: Mon, 18 Nov 2024 12:14:03 +0000 Subject: [PATCH 2/2] animation/animation.go: Move to world package & rename to EntityAnimation --- server/entity/animation/animation.go | 63 ---------------------------- server/session/world.go | 3 +- server/world/entity_animation.go | 63 ++++++++++++++++++++++++++++ server/world/viewer.go | 5 +-- 4 files changed, 66 insertions(+), 68 deletions(-) delete mode 100644 server/entity/animation/animation.go create mode 100644 server/world/entity_animation.go diff --git a/server/entity/animation/animation.go b/server/entity/animation/animation.go deleted file mode 100644 index 11bde71c1..000000000 --- a/server/entity/animation/animation.go +++ /dev/null @@ -1,63 +0,0 @@ -package animation - -// Animation represents an animation that may be played on an entity from an active resource pack on -// the client. -type Animation struct { - name string - nextState string - controller string - stopCondition string -} - -// New returns a new animation that can be played on an entity. If no controller or stop condition is set, -// the animation will play for its full duration, including looping. Controllers can be set to manage -// multiple states of animations. It is also possible to use vanilla animations/controllers if they work -// for your entity, i.e. "animation.pig.baby_transform". -func New(name string) Animation { - return Animation{name: name} -} - -// Name returns the name of the animation to be played. -func (a Animation) Name() string { - return a.name -} - -// Controller returns the name of the controller to be used for the animation. -func (a Animation) Controller() string { - return a.controller -} - -// WithController returns a copy of the Animation with the provided animation controller. An animation -// controller with the same name must be defined in a resource pack for it to work. -func (a Animation) WithController(controller string) Animation { - a.controller = controller - return a -} - -// NextState returns the state to transition to after the animation has finished playing within the -// animation controller. -func (a Animation) NextState() string { - return a.nextState -} - -// WithNextState returns a copy of the Animation with the provided state to transition to after the -// animation has finished playing within the animation controller. -func (a Animation) WithNextState(state string) Animation { - a.nextState = state - return a -} - -// StopCondition returns the condition that must be met for the animation to stop playing. This is often -// a Molang expression that can be used to query various entity properties to determine when the animation -// should stop playing. -func (a Animation) StopCondition() string { - return a.stopCondition -} - -// WithStopCondition returns a copy of the Animation with the provided stop condition. The stop condition -// is a Molang expression that can be used to query various entity properties to determine when the animation -// should stop playing. -func (a Animation) WithStopCondition(condition string) Animation { - a.stopCondition = condition - return a -} diff --git a/server/session/world.go b/server/session/world.go index 2770567df..e4628bb13 100644 --- a/server/session/world.go +++ b/server/session/world.go @@ -1,7 +1,6 @@ package session import ( - "github.com/df-mc/dragonfly/server/entity/animation" "github.com/df-mc/dragonfly/server/entity/effect" "image/color" "math/rand" @@ -977,7 +976,7 @@ func (s *Session) ViewEntityState(e world.Entity) { } // ViewEntityAnimation ... -func (s *Session) ViewEntityAnimation(e world.Entity, a animation.Animation) { +func (s *Session) ViewEntityAnimation(e world.Entity, a world.EntityAnimation) { s.writePacket(&packet.AnimateEntity{ Animation: a.Name(), NextState: a.NextState(), diff --git a/server/world/entity_animation.go b/server/world/entity_animation.go new file mode 100644 index 000000000..b9f459c33 --- /dev/null +++ b/server/world/entity_animation.go @@ -0,0 +1,63 @@ +package world + +// EntityAnimation represents an animation that may be played on an entity from an active resource pack on +// the client. +type EntityAnimation struct { + name string + nextState string + controller string + stopCondition string +} + +// NewEntityAnimation returns a new animation that can be played on an entity. If no controller or stop +// condition is set, the animation will play for its full duration, including looping. Controllers can be set +// to manage multiple states of animations. It is also possible to use vanilla animations/controllers if they +// work for your entity, i.e. "animation.pig.baby_transform". +func NewEntityAnimation(name string) EntityAnimation { + return EntityAnimation{name: name} +} + +// Name returns the name of the animation to be played. +func (a EntityAnimation) Name() string { + return a.name +} + +// Controller returns the name of the controller to be used for the animation. +func (a EntityAnimation) Controller() string { + return a.controller +} + +// WithController returns a copy of the EntityAnimation with the provided animation controller. An animation +// controller with the same name must be defined in a resource pack for it to work. +func (a EntityAnimation) WithController(controller string) EntityAnimation { + a.controller = controller + return a +} + +// NextState returns the state to transition to after the animation has finished playing within the +// animation controller. +func (a EntityAnimation) NextState() string { + return a.nextState +} + +// WithNextState returns a copy of the EntityAnimation with the provided state to transition to after the +// animation has finished playing within the animation controller. +func (a EntityAnimation) WithNextState(state string) EntityAnimation { + a.nextState = state + return a +} + +// StopCondition returns the condition that must be met for the animation to stop playing. This is often +// a Molang expression that can be used to query various entity properties to determine when the animation +// should stop playing. +func (a EntityAnimation) StopCondition() string { + return a.stopCondition +} + +// WithStopCondition returns a copy of the EntityAnimation with the provided stop condition. The stop condition +// is a Molang expression that can be used to query various entity properties to determine when the animation +// should stop playing. +func (a EntityAnimation) WithStopCondition(condition string) EntityAnimation { + a.stopCondition = condition + return a +} diff --git a/server/world/viewer.go b/server/world/viewer.go index 8d20cf007..7ffbbbebf 100644 --- a/server/world/viewer.go +++ b/server/world/viewer.go @@ -2,7 +2,6 @@ package world import ( "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" @@ -51,7 +50,7 @@ type Viewer interface { // physical appearance, for example when sprinting. ViewEntityState(e Entity) // ViewEntityAnimation starts viewing an animation performed by an Entity. - ViewEntityAnimation(e Entity, a animation.Animation) + ViewEntityAnimation(e Entity, a EntityAnimation) // 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) @@ -92,7 +91,7 @@ func (NopViewer) ViewEntityItems(Entity) func (NopViewer) ViewEntityArmour(Entity) {} func (NopViewer) ViewEntityAction(Entity, EntityAction) {} func (NopViewer) ViewEntityState(Entity) {} -func (NopViewer) ViewEntityAnimation(Entity, animation.Animation) {} +func (NopViewer) ViewEntityAnimation(Entity, EntityAnimation) {} func (NopViewer) ViewParticle(mgl64.Vec3, Particle) {} func (NopViewer) ViewSound(mgl64.Vec3, Sound) {} func (NopViewer) ViewBlockUpdate(cube.Pos, Block, int) {}