Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce entity animations #806

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 61 additions & 0 deletions server/entity/animation/animation.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package animation

// Animation represents an animation & controller that may be attached to an entity.
// Animations and controllers must be defined in a resource pack
Comment on lines +3 to +4
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something like this would sound better

// Animation represents an animation that may be played on an entity from an active resource pack on
// the client.

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(animationName string) Animation {
return Animation{
name: animationName,
state: "",
controller: "",
stopCondition: "",
}
Comment on lines +13 to +18
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can just be Animation{name: name}

}

// 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
Comment on lines +26 to +27
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// WithController returns a copy of the Animation with the provided animation controller. The
// controller must be defined in a resource pack for it to work.

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
Comment on lines +33 to +34
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't think it's necessary to mention the empty string here, also purely just for OCD reasons could you move Controller(), State() and StopCondition() methods to be before their respective With...() methods?

func (a Animation) Controller() string {
return a.controller
}

// WithState sets the state to transition to as defined in the controller.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// WithState returns a copy of the Animation with the provided next state.

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
Comment on lines +45 to +46
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same thing, no point mentioning empty string. This is also incorrect since it's not the state that is currently being played. Something like this may be better:

// State returns the name of the next state in the animation controller.

func (a Animation) State() string {
return a.state
}

// WithStopCondition takes the molang expression and stops the animation if the query passes.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// WithStopCondition returns a copy of the Animation with the provided stop condition. This condition is
// usually a Molang query which is constantly compared against until the animation has stopped.

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
Comment on lines +57 to +58
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same thing, no point mentioning empty string

func (a Animation) StopCondition() string {
return a.stopCondition
}
11 changes: 8 additions & 3 deletions server/session/world.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
package session

import (
"github.com/df-mc/dragonfly/server/entity/effect"
"image/color"
"math/rand"
"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"
"github.com/df-mc/dragonfly/server/block/cube"
"github.com/df-mc/dragonfly/server/entity"
Expand Down Expand Up @@ -915,9 +917,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, animation animation.Animation) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd just rename this variable to a to avoid conflicting with the package, same thing in the interface yupe in viewer.go

s.writePacket(&packet.AnimateEntity{
Animation: animationName,
Animation: animation.Name(),
NextState: animation.State(),
StopCondition: animation.StopCondition(),
Controller: animation.Controller(),
EntityRuntimeIDs: []uint64{
s.entityRuntimeID(e),
},
Expand Down
8 changes: 5 additions & 3 deletions server/world/viewer.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package world

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"
"time"
)

// Viewer is a viewer in the world. It can view changes that are made in the world, such as the addition of
Expand Down Expand Up @@ -48,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 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)
Expand Down Expand Up @@ -89,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) {}
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) {}
Expand Down
17 changes: 16 additions & 1 deletion server/world/world.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -643,6 +645,19 @@ func (w *World) AddParticle(pos mgl64.Vec3, p Particle) {
}
}

// AnimateEntity will start an animation for the specified entity. Viewers that are viewing the entity will be
// played the animation.
func (w *World) AnimateEntity(e Entity, animation animation.Animation) {
Comment on lines +648 to +650
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PlayEntityAnimation might be a better name to fit with PlaySound etc.

// Ignore if no animation name has been given
if animation.Name() == "" {
TwistedAsylumMC marked this conversation as resolved.
Show resolved Hide resolved
return
}

for _, v := range w.Viewers(e.Position()) {
v.ViewEntityAnimation(e, animation)
}
}

// 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) {
Expand Down