Skip to content

Commit

Permalink
Multi bossbar support
Browse files Browse the repository at this point in the history
  • Loading branch information
AkmalFairuz committed Jul 20, 2024
1 parent 56780ca commit b705fcb
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 26 deletions.
13 changes: 9 additions & 4 deletions server/player/player.go
Original file line number Diff line number Diff line change
Expand Up @@ -344,14 +344,19 @@ func (p *Player) RemoveScoreboard() {
// SendBossBar sends a boss bar to the player, so that it will be shown indefinitely at the top of the
// player's screen.
// The boss bar may be removed by calling Player.RemoveBossBar().
func (p *Player) SendBossBar(bar bossbar.BossBar) {
p.session().SendBossBar(bar.Text(), bar.Colour().Uint8(), bar.HealthPercentage())
func (p *Player) SendBossBar(channel int, bar bossbar.BossBar) {
p.session().SendBossBar(channel, bar.Text(), bar.Colour().Uint8(), bar.HealthPercentage())
}

// RemoveBossBar removes any boss bar currently active on the player's screen. If no boss bar is currently
// present, nothing happens.
func (p *Player) RemoveBossBar() {
p.session().RemoveBossBar()
func (p *Player) RemoveBossBar(channel int) {
p.session().RemoveBossBar(channel)
}

// BossBarChannelIDs returns a list of all channel IDs that are currently active on the player's screen.
func (p *Player) BossBarChannelIDs() []int {
return p.session().BossBarChannelIDs()
}

// Chat writes a message in the global chat (chat.Global). The message is prefixed with the name of the
Expand Down
40 changes: 23 additions & 17 deletions server/session/session.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ type Session struct {
joinMessage, quitMessage string

closeBackground chan struct{}

bossBarChannelRuntimeIDs map[int]uint64
}

// Conn represents a connection that packets are read from and written to by a Session. In addition, it holds some
Expand Down Expand Up @@ -148,23 +150,24 @@ func New(conn Conn, maxChunkRadius int, log Logger, joinMessage, quitMessage str

s := &Session{}
*s = Session{
openChunkTransactions: make([]map[uint64]struct{}, 0, 8),
closeBackground: make(chan struct{}),
ui: inventory.New(54, s.handleInterfaceUpdate),
handlers: map[uint32]packetHandler{},
entityRuntimeIDs: map[world.Entity]uint64{},
entities: map[uint64]world.Entity{},
hiddenEntities: map[world.Entity]struct{}{},
blobs: map[uint64][]byte{},
chunkRadius: int32(r),
maxChunkRadius: int32(maxChunkRadius),
conn: conn,
log: log,
currentEntityRuntimeID: 1,
heldSlot: atomic.NewUint32(0),
joinMessage: joinMessage,
quitMessage: quitMessage,
openedWindow: *atomic.NewValue(inventory.New(1, nil)),
openChunkTransactions: make([]map[uint64]struct{}, 0, 8),
closeBackground: make(chan struct{}),
ui: inventory.New(54, s.handleInterfaceUpdate),
handlers: map[uint32]packetHandler{},
entityRuntimeIDs: map[world.Entity]uint64{},
entities: map[uint64]world.Entity{},
hiddenEntities: map[world.Entity]struct{}{},
blobs: map[uint64][]byte{},
chunkRadius: int32(r),
maxChunkRadius: int32(maxChunkRadius),
conn: conn,
log: log,
currentEntityRuntimeID: 1,
heldSlot: atomic.NewUint32(0),
joinMessage: joinMessage,
quitMessage: quitMessage,
openedWindow: *atomic.NewValue(inventory.New(1, nil)),
bossBarChannelRuntimeIDs: map[int]uint64{},
}

s.registerHandlers()
Expand Down Expand Up @@ -526,6 +529,9 @@ func (s *Session) sendAvailableEntities(w *world.World) {
for _, t := range w.EntityRegistry().Types() {
identifiers = append(identifiers, actorIdentifier{ID: t.EncodeEntity()})
}
// TODO: "minecraft:slime" is used to send the boss bar. This should not be hardcoded.
// Remove this when slime is supported.
identifiers = append(identifiers, actorIdentifier{ID: "minecraft:slime"})
serializedEntityData, err := nbt.Marshal(map[string]any{"idlist": identifiers})
if err != nil {
panic("should never happen")
Expand Down
45 changes: 40 additions & 5 deletions server/session/text.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,10 +116,31 @@ func (s *Session) RemoveScoreboard() {

// SendBossBar sends a boss bar to the player with the text passed and the health percentage of the bar.
// SendBossBar removes any boss bar that might be active before sending the new one.
func (s *Session) SendBossBar(text string, colour uint8, healthPercentage float64) {
s.RemoveBossBar()
func (s *Session) SendBossBar(channel int, text string, colour uint8, healthPercentage float64) {
s.RemoveBossBar(channel)
s.currentEntityRuntimeID += 1
runtimeID := s.currentEntityRuntimeID
s.bossBarChannelRuntimeIDs[channel] = runtimeID

m := protocol.EntityMetadata{}
m.SetFlag(protocol.EntityDataKeyFlags, protocol.EntityDataFlagInvisible)
m.SetFlag(protocol.EntityDataKeyFlags, protocol.EntityDataFlagSilent)
m.SetFlag(protocol.EntityDataKeyFlags, protocol.EntityDataFlagNoAI)
m.SetFlag(protocol.EntityDataKeyFlags, protocol.EntityDataFlagFireImmune)
m[protocol.EntityDataKeyName] = ""
m[protocol.EntityDataKeyScale] = float32(0)
m[protocol.EntityDataKeyWidth] = float32(0)
m[protocol.EntityDataKeyHeight] = float32(0)
m[protocol.EntityDataKeyLeashHolder] = int64(-1)

s.writePacket(&packet.AddActor{
EntityUniqueID: int64(runtimeID),
EntityRuntimeID: runtimeID,
EntityType: "minecraft:slime", // TODO: Do not hardcode
EntityMetadata: m,
})
s.writePacket(&packet.BossEvent{
BossEntityUniqueID: selfEntityRuntimeID,
BossEntityUniqueID: int64(runtimeID),
EventType: packet.BossEventShow,
BossBarTitle: text,
HealthPercentage: float32(healthPercentage),
Expand All @@ -128,11 +149,25 @@ func (s *Session) SendBossBar(text string, colour uint8, healthPercentage float6
}

// RemoveBossBar removes any boss bar currently active on the player's screen.
func (s *Session) RemoveBossBar() {
func (s *Session) RemoveBossBar(channel int) {
runtimeID, ok := s.bossBarChannelRuntimeIDs[channel]
if !ok {
return
}
s.writePacket(&packet.BossEvent{
BossEntityUniqueID: selfEntityRuntimeID,
BossEntityUniqueID: int64(runtimeID),
EventType: packet.BossEventHide,
})
s.writePacket(&packet.RemoveActor{EntityUniqueID: int64(runtimeID)})
}

// BossBarChannelIDs returns a list of all boss bar channel IDs that are currently active on the player's screen.
func (s *Session) BossBarChannelIDs() []int {
var ids []int
for id := range s.bossBarChannelRuntimeIDs {
ids = append(ids, id)
}
return ids
}

const tickLength = time.Second / 20
Expand Down

0 comments on commit b705fcb

Please sign in to comment.