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

Filled maps (non-persistent, not rendered from terrain) #478

Draft
wants to merge 60 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 46 commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
179eac8
Filled map
Endermanbugzjfc Apr 23, 2022
6ae813e
Register filled map
Endermanbugzjfc Apr 23, 2022
7f40354
Removed IsInit
Endermanbugzjfc Apr 23, 2022
01cc34b
Fixed doc for is scaling
Endermanbugzjfc Apr 23, 2022
bc67095
Revert "Removed IsInit"
Endermanbugzjfc Apr 23, 2022
cb9feca
Map interface
Endermanbugzjfc Apr 23, 2022
c6ea0c8
Added getters
Endermanbugzjfc Apr 23, 2022
3b37890
Added other types of filled map
Endermanbugzjfc Apr 23, 2022
ab45192
Extracted baseMap from FilledMap
Endermanbugzjfc Apr 23, 2022
7e38be5
Added map interface
Endermanbugzjfc Apr 23, 2022
19434a1
Added MapInfoRequestHandler (Not yet implemented)
Endermanbugzjfc Apr 23, 2022
4a43519
Error if player does not have corresponding map item
Endermanbugzjfc May 1, 2022
b1b723d
Rewrote map registration and update API
Endermanbugzjfc May 1, 2022
012840d
Specify the data type of ID
Endermanbugzjfc May 1, 2022
4c8f682
Forget to complete doc comment
Endermanbugzjfc May 1, 2022
34e6d55
Doc the update map function
Endermanbugzjfc May 1, 2022
4de8462
Fixed grammar (broadcast => broadcasts)
Endermanbugzjfc May 1, 2022
84d3f57
Added a map ID argument
Endermanbugzjfc May 1, 2022
6173cde
Merge remote-tracking branch 'upstream/master' into map-item
Endermanbugzjfc Jul 21, 2022
5d8f189
Moved viewer / data related API to item package
Endermanbugzjfc Jul 21, 2022
be68fde
Update doc comment and adding missing methods
Endermanbugzjfc Jul 21, 2022
62fe937
Delete viewer method
Endermanbugzjfc Jul 21, 2022
f7370aa
Added get data method
Endermanbugzjfc Jul 21, 2022
66c8c0b
Viewers mutex
Endermanbugzjfc Jul 21, 2022
811ef42
Write client bound map item data packet of initialisation
Endermanbugzjfc Jul 21, 2022
e1acf10
Forgot to stage map.go
Endermanbugzjfc Jul 21, 2022
b3aa6e3
Tracked objects
Endermanbugzjfc Jul 21, 2022
4a2225b
session.sendMapDataUpdate()
Endermanbugzjfc Jul 21, 2022
d57c6ae
X and Y offset
Endermanbugzjfc Jul 21, 2022
e8b2c65
Locked status in map data
Endermanbugzjfc Jul 22, 2022
f9f6d33
GetData() method
Endermanbugzjfc Jul 22, 2022
66ffb52
Not to allow changing scale or locked status of existed map data
Endermanbugzjfc Jul 22, 2022
b596d7d
Not to expose map ID to the API
Endermanbugzjfc Jul 22, 2022
9e77068
Nbtconv not allowed in item package :(
Endermanbugzjfc Jul 22, 2022
eef7e94
Merge branch 'df-mc:master' into map-item
Endermanbugzjfc Jul 22, 2022
fcb224c
Merge branch 'map-item' of https://github.com/crnw-dev/dragonfly into…
Endermanbugzjfc Jul 22, 2022
ed59a46
Fixed world package importing itself
Endermanbugzjfc Jul 22, 2022
b976bd3
Map pixels chunk struct
Endermanbugzjfc Jul 22, 2022
8b265cc
Map data creation and loading
Endermanbugzjfc Jul 22, 2022
5db4802
Allocate maps on viewable map data pointer creation
Endermanbugzjfc Jul 22, 2022
05268ce
Fixed usage of LoadMapData() result
Endermanbugzjfc Jul 22, 2022
b20a8ab
Fixed map info request handler
Endermanbugzjfc Jul 22, 2022
8c86240
Remove viewer who no longer has access to map data
Endermanbugzjfc Jul 22, 2022
31d9071
Fixed doc comment
Endermanbugzjfc Jul 22, 2022
c280ba4
Merge branch 'master' into map-item
Endermanbugzjfc Jul 23, 2022
ec77694
Added Bearing()
Endermanbugzjfc Jul 23, 2022
2254b3c
Revert "Added Bearing()"
Endermanbugzjfc Jul 26, 2022
ab96ac0
Track entities and blocks now hold their decoration offsets
Endermanbugzjfc Jul 26, 2022
d372715
Send decorations
Endermanbugzjfc Jul 26, 2022
80c3bbd
Merge branch 'master' into map-item
Endermanbugzjfc Jul 26, 2022
02df08e
Fixed doc comment
Endermanbugzjfc Jul 26, 2022
ad4d7a3
Allow changing tracked objects without sending
Endermanbugzjfc Jul 26, 2022
21801fb
Doc comment for MapPixelsChunk
Endermanbugzjfc Jul 26, 2022
c39cb14
Removed comment
Endermanbugzjfc Jul 28, 2022
52390d7
Merge branch 'master' into map-item
Endermanbugzjfc Jul 28, 2022
7b21511
Map ID equals always false if data is nil
Endermanbugzjfc Jul 30, 2022
bbed433
Do not use map ID 0
Endermanbugzjfc Jul 30, 2022
61222d6
Locked map
Endermanbugzjfc Jul 31, 2022
228f42c
Returns pointer instead of value copy
Endermanbugzjfc Jul 31, 2022
e54056d
Function name conflicts with type
Endermanbugzjfc Jul 31, 2022
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
19 changes: 18 additions & 1 deletion server/entity/direction.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
package entity

import (
"math"

"github.com/df-mc/dragonfly/server/block/cube"
"github.com/df-mc/dragonfly/server/world"
"github.com/go-gl/mathgl/mgl64"
"math"
)

// Facing returns the horizontal direction that an entity is facing.
Expand All @@ -27,6 +28,22 @@ func Facing(e world.Entity) cube.Direction {
return 0
}

// Bearing splits the 360 degree to sections and returns the section number by yaw.
func Bearing(yaw float64, max int64) int64 {
Endermanbugzjfc marked this conversation as resolved.
Show resolved Hide resolved
// Ported from from https://stackoverflow.com/a/38505683.
// https://code.sololearn.com/cLibMMRbbnb2.
roundedYaw := int64(math.Round(yaw))

divisor := 360 / max
coci := roundedYaw / divisor
resto := roundedYaw % divisor
if resto <= divisor/2 {
return coci % max
} else {
return (coci + 1) % max
}
}

// DirectionVector returns a vector that describes the direction of the entity passed. The length of the Vec3
// returned is always 1.
func DirectionVector(e world.Entity) mgl64.Vec3 {
Expand Down
10 changes: 10 additions & 0 deletions server/item/filled_map.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package item

type FilledMap struct {
BaseMap
}

// EncodeItem ...
func (m FilledMap) EncodeItem() (name string, meta int16) {
return "minecraft:filled_map", 0
}
46 changes: 46 additions & 0 deletions server/item/map.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package item

import (
"github.com/df-mc/dragonfly/server/world"
)

type MapItem interface {
BaseMap() BaseMap
}

type BaseMap struct {
// IsInit has unknown functionality (referring to the Minecraft Wiki).
IsInit bool
// NameIndex is the index of the map's name.
NameIndex int32
// DisplayPlayers controls whether the map displays player markers (depends on Decorations and TrackedObjects in the map data).
DisplayPlayers bool

*world.ViewableMapData
}

// DecodeNBT ...
func (m BaseMap) DecodeNBT(data map[string]any) any {
m.IsInit, _ = data["map_is_init"].(bool)
m.NameIndex, _ = data["map_name_index"].(int32)
m.DisplayPlayers, _ = data["map_display_players"].(bool)
/*
if id, ok := data["map_uuid"].(int64); ok {
} TODO: Persisted map data.
*/
return m
}

// EncodeNBT ...
func (m BaseMap) EncodeNBT() map[string]any {
data := m.ViewableMapData.EncodeItemNBT()
data["map_is_init"] = m.IsInit
data["map_name_index"] = m.NameIndex
data["map_display_players"] = m.DisplayPlayers
return data
}

// BaseMap ...
func (m BaseMap) BaseMap() BaseMap {
return m
}
10 changes: 10 additions & 0 deletions server/item/ocean_explorer_map.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package item

type OceanMap struct {
BaseMap
}

// EncodeItem ...
func (m OceanMap) EncodeItem() (name string, meta int16) {
return "minecraft:filled_map", 3
}
5 changes: 5 additions & 0 deletions server/item/register.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,4 +151,9 @@ func init() {
world.RegisterItem(AmethystShard{})
world.RegisterItem(DiscFragment{})
world.RegisterItem(EchoShard{})

world.RegisterItem(FilledMap{})
world.RegisterItem(OceanMap{})
world.RegisterItem(WoodlandExplorerMap{})
world.RegisterItem(TreasureMap{})
}
10 changes: 10 additions & 0 deletions server/item/treasure_map.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package item

type TreasureMap struct {
BaseMap
}

// EncodeItem ...
func (m TreasureMap) EncodeItem() (name string, meta int16) {
return "minecraft:filled_map", 5
}
10 changes: 10 additions & 0 deletions server/item/woodland_explorer_map.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package item

type WoodlandExplorerMap struct {
BaseMap
}

// EncodeItem ...
func (m WoodlandExplorerMap) EncodeItem() (name string, meta int16) {
return "minecraft:filled_map", 4
}
30 changes: 30 additions & 0 deletions server/session/handler_map_info_request.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package session

import (
"fmt"

"github.com/df-mc/dragonfly/server/item"
"github.com/df-mc/dragonfly/server/world"
"github.com/sandertv/gophertunnel/minecraft/protocol/packet"
)

// MapInfoRequestHandler handles the MapInfoRequest packet.
type MapInfoRequestHandler struct {
}

// Handle ...
func (h *MapInfoRequestHandler) Handle(p packet.Packet, s *Session) error {
var (
pk = p.(*packet.MapInfoRequest)
ok bool
mapItem item.MapItem
)
if mapItem, ok = s.canAccessMapData(pk.MapID); !ok {
return fmt.Errorf("client requests info of map %v while he does not have the corresponding map item in inventory, off hand inventory, UI inventory or armour inventory", pk.MapID)
}

mapItem.BaseMap().AddViewer(s)
s.SendMapData(packet.MapUpdateFlagInitialisation, pk.MapID, world.MapPixelsChunk{}, mapItem.BaseMap().ViewableMapData)

return nil
}
87 changes: 83 additions & 4 deletions server/session/session.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ import (
"context"
"errors"
"fmt"
"io"
"net"
"sync"
"time"

"github.com/df-mc/atomic"
"github.com/df-mc/dragonfly/server/block"
"github.com/df-mc/dragonfly/server/block/cube"
Expand All @@ -21,10 +26,6 @@ import (
"github.com/sandertv/gophertunnel/minecraft/protocol/login"
"github.com/sandertv/gophertunnel/minecraft/protocol/packet"
"github.com/sandertv/gophertunnel/minecraft/text"
"io"
"net"
"sync"
"time"
)

// Session handles incoming packets from connections and sends outgoing packets by providing a thin layer
Expand Down Expand Up @@ -463,6 +464,7 @@ func (s *Session) registerHandlers() {
packet.IDItemFrameDropItem: nil,
packet.IDItemStackRequest: &ItemStackRequestHandler{changes: make(map[byte]map[byte]changeInfo), responseChanges: map[int32]map[*inventory.Inventory]map[byte]responseChange{}},
packet.IDLevelSoundEvent: &LevelSoundEventHandler{},
packet.IDMapInfoRequest: &MapInfoRequestHandler{},
packet.IDMobEquipment: &MobEquipmentHandler{},
packet.IDModalFormResponse: &ModalFormResponseHandler{forms: make(map[uint32]form.Form)},
packet.IDMovePlayer: nil,
Expand Down Expand Up @@ -546,3 +548,80 @@ func (s *Session) sendAvailableEntities() {
}
s.writePacket(&packet.AvailableActorIdentifiers{SerialisedEntityIdentifiers: serializedEntityData})
}

// ViewMapDataChange writes *packet.ClientBoundMapItemData if session still has access to the data.
func (s *Session) ViewMapDataChange(updateFlag uint32, mapID int64, pixelsChunk world.MapPixelsChunk, data *world.ViewableMapData) {
if _, ok := s.canAccessMapData(mapID); !ok {
data.RemoveViewer(s)
return
}

s.SendMapData(updateFlag, mapID, pixelsChunk, data)
}

// SendMapData writes *packet.ClientBoundMapItemData.
func (s *Session) SendMapData(updateFlag uint32, mapID int64, pixelsChunk world.MapPixelsChunk, data *world.ViewableMapData) {
var (
d = data.MapData()
trackeds []protocol.MapTrackedObject
)
for e := range d.TrackEntities {
// Since entity IDs are not reused per world, I am not checking world here.
trackeds = append(trackeds, protocol.MapTrackedObject{
Type: protocol.MapObjectTypeEntity,
EntityUniqueID: int64(s.entityRuntimeID(e)),
})
}
if data.World() == s.c.World() {
for p := range d.TrackBlocks {
trackeds = append(trackeds, protocol.MapTrackedObject{
Type: protocol.MapObjectTypeBlock,
BlockPosition: protocol.BlockPos{int32(p[0]), int32(p[1]), int32(p[2])},
})
}
}

s.writePacket(&packet.ClientBoundMapItemData{
MapID: mapID,
UpdateFlags: updateFlag,
Dimension: byte(data.World().Dimension().EncodeDimension()),
LockedMap: d.Locked,
Scale: d.Scale,
TrackedObjects: trackeds,
// Decorations is a list of fixed decorations located on the map. The decorations will not change
// client-side, unless the server updates them.
Decorations: []protocol.MapDecoration{},

Height: pixelsChunk.Height,
Width: pixelsChunk.Width,
XOffset: pixelsChunk.XOffset,
YOffset: pixelsChunk.YOffset,
Pixels: pixelsChunk.Pixels,
})

}

func (s *Session) canAccessMapData(mapID int64) (item.MapItem, bool) {
var (
mapItem item.MapItem
ok bool
)
for _, inv := range []*inventory.Inventory{
s.inv,
s.offHand,
s.ui,
s.armour.Inventory(),
} {
if inv.ContainsItemFunc(1, func(stack item.Stack) bool {
if mapItem, ok = stack.Item().(item.MapItem); ok {
return mapItem.BaseMap().MapIDEquals(mapID)
}

return false // Item is not map.
}) {
return mapItem, true
}
}

return nil, false
}
Loading