Finish adding all 0.7 snap items that are actually sent

This commit is contained in:
ChillerDragon 2024-06-24 22:50:15 +08:00
parent 0edc8b334b
commit 141865538f
22 changed files with 697 additions and 32 deletions

View file

@ -13,6 +13,11 @@ const (
TeamRed GameTeam = 0 TeamRed GameTeam = 0
TeamBlue GameTeam = 1 TeamBlue GameTeam = 1
SpecFreeview Spec = 0
SpecPlayer Spec = 1
SpecFlagred Spec = 2
SpecFlagblue Spec = 3
VoteUnknown Vote = 0 VoteUnknown Vote = 0
VoteStartOp Vote = 1 VoteStartOp Vote = 1
VoteStartKick Vote = 2 VoteStartKick Vote = 2
@ -191,6 +196,7 @@ const (
) )
type GameStateFlag int type GameStateFlag int
type Spec int
type Vote int type Vote int
type Pickup int type Pickup int
type Emote int type Emote int

View file

@ -39,7 +39,7 @@ func (o *Character) Id() int {
return o.ItemId return o.ItemId
} }
func (o *Character) Type() int { func (o *Character) TypeId() int {
return network7.ObjCharacter return network7.ObjCharacter
} }
@ -49,7 +49,7 @@ func (o *Character) Size() int {
func (o *Character) Pack() []byte { func (o *Character) Pack() []byte {
return slices.Concat( return slices.Concat(
packer.PackInt(o.Type()), packer.PackInt(o.TypeId()),
packer.PackInt(o.Id()), packer.PackInt(o.Id()),
packer.PackInt(o.Tick), packer.PackInt(o.Tick),

64
object7/damage.go Normal file
View file

@ -0,0 +1,64 @@
package object7
import (
"reflect"
"slices"
"github.com/teeworlds-go/go-teeworlds-protocol/network7"
"github.com/teeworlds-go/go-teeworlds-protocol/packer"
)
// damage indicicator
// displayed as yellow stars around the tee receiving damage
type Damage struct {
ItemId int
X int
Y int
// affected player receiving damage
ClientId int
Angle int
HealthAmount int
ArmorAmount int
// true if the damage receiver the damage dealer
Self bool
}
func (o *Damage) Id() int {
return o.ItemId
}
func (o *Damage) TypeId() int {
return network7.ObjDamage
}
func (o *Damage) Size() int {
return reflect.TypeOf(Damage{}).NumField() - 1
}
func (o *Damage) Pack() []byte {
return slices.Concat(
packer.PackInt(o.TypeId()),
packer.PackInt(o.Id()),
packer.PackInt(o.X),
packer.PackInt(o.Y),
packer.PackInt(o.ClientId),
packer.PackInt(o.Angle),
packer.PackInt(o.HealthAmount),
packer.PackInt(o.ArmorAmount),
packer.PackBool(o.Self),
)
}
func (o *Damage) Unpack(u *packer.Unpacker) error {
o.X = u.GetInt()
o.Y = u.GetInt()
o.ClientId = u.GetInt()
o.Angle = u.GetInt()
o.HealthAmount = u.GetInt()
o.ArmorAmount = u.GetInt()
o.Self = u.GetInt() != 0
return nil
}

48
object7/death.go Normal file
View file

@ -0,0 +1,48 @@
package object7
import (
"reflect"
"slices"
"github.com/teeworlds-go/go-teeworlds-protocol/network7"
"github.com/teeworlds-go/go-teeworlds-protocol/packer"
)
type Death struct {
ItemId int
X int
Y int
ClientId int
}
func (o *Death) Id() int {
return o.ItemId
}
func (o *Death) TypeId() int {
return network7.ObjDeath
}
func (o *Death) Size() int {
return reflect.TypeOf(Death{}).NumField() - 1
}
func (o *Death) Pack() []byte {
return slices.Concat(
packer.PackInt(o.TypeId()),
packer.PackInt(o.Id()),
packer.PackInt(o.X),
packer.PackInt(o.Y),
packer.PackInt(o.ClientId),
)
}
func (o *Death) Unpack(u *packer.Unpacker) error {
o.X = u.GetInt()
o.Y = u.GetInt()
o.ClientId = u.GetInt()
return nil
}

45
object7/explosion.go Normal file
View file

@ -0,0 +1,45 @@
package object7
import (
"reflect"
"slices"
"github.com/teeworlds-go/go-teeworlds-protocol/network7"
"github.com/teeworlds-go/go-teeworlds-protocol/packer"
)
type Explosion struct {
ItemId int
X int
Y int
}
func (o *Explosion) Id() int {
return o.ItemId
}
func (o *Explosion) TypeId() int {
return network7.ObjExplosion
}
func (o *Explosion) Size() int {
return reflect.TypeOf(Explosion{}).NumField() - 1
}
func (o *Explosion) Pack() []byte {
return slices.Concat(
packer.PackInt(o.TypeId()),
packer.PackInt(o.Id()),
packer.PackInt(o.X),
packer.PackInt(o.Y),
)
}
func (o *Explosion) Unpack(u *packer.Unpacker) error {
o.X = u.GetInt()
o.Y = u.GetInt()
return nil
}

View file

@ -20,7 +20,7 @@ func (o *Flag) Id() int {
return o.ItemId return o.ItemId
} }
func (o *Flag) Type() int { func (o *Flag) TypeId() int {
return network7.ObjFlag return network7.ObjFlag
} }
@ -30,7 +30,7 @@ func (o *Flag) Size() int {
func (o *Flag) Pack() []byte { func (o *Flag) Pack() []byte {
return slices.Concat( return slices.Concat(
packer.PackInt(o.Type()), packer.PackInt(o.TypeId()),
packer.PackInt(o.Id()), packer.PackInt(o.Id()),
packer.PackInt(o.X), packer.PackInt(o.X),

View file

@ -25,7 +25,7 @@ func (o *GameData) Id() int {
return o.ItemId return o.ItemId
} }
func (o *GameData) Type() int { func (o *GameData) TypeId() int {
return network7.ObjGameData return network7.ObjGameData
} }
@ -35,7 +35,7 @@ func (o *GameData) Size() int {
func (o *GameData) Pack() []byte { func (o *GameData) Pack() []byte {
return slices.Concat( return slices.Concat(
packer.PackInt(o.Type()), packer.PackInt(o.TypeId()),
packer.PackInt(o.Id()), packer.PackInt(o.Id()),
packer.PackInt(o.GameStartTick), packer.PackInt(o.GameStartTick),

View file

@ -33,7 +33,7 @@ func (o *GameDataFlag) Id() int {
return o.ItemId return o.ItemId
} }
func (o *GameDataFlag) Type() int { func (o *GameDataFlag) TypeId() int {
return network7.ObjGameDataFlag return network7.ObjGameDataFlag
} }
@ -43,7 +43,7 @@ func (o *GameDataFlag) Size() int {
func (o *GameDataFlag) Pack() []byte { func (o *GameDataFlag) Pack() []byte {
return slices.Concat( return slices.Concat(
packer.PackInt(o.Type()), packer.PackInt(o.TypeId()),
packer.PackInt(o.Id()), packer.PackInt(o.Id()),
packer.PackInt(o.FlagCarrierRed), packer.PackInt(o.FlagCarrierRed),

56
object7/game_data_race.go Normal file
View file

@ -0,0 +1,56 @@
package object7
import (
"reflect"
"slices"
"github.com/teeworlds-go/go-teeworlds-protocol/network7"
"github.com/teeworlds-go/go-teeworlds-protocol/packer"
)
// this is a new snap item that was added after the 0.7 release
// so for backwards compability it includes a size field
// and older clients ignore it
//
// this message is not used by official servers
// and is part of an effort to support community made race modifications
type GameDataRace struct {
ItemId int
BestTime int
Precision int
RaceFlags int
}
func (o *GameDataRace) Id() int {
return o.ItemId
}
func (o *GameDataRace) TypeId() int {
return network7.ObjGameDataRace
}
func (o *GameDataRace) Size() int {
// TODO: is this correct? is this just payload size or does it contain the size field as well?
return reflect.TypeOf(GameDataRace{}).NumField() - 1
}
func (o *GameDataRace) Pack() []byte {
return slices.Concat(
packer.PackInt(o.TypeId()),
packer.PackInt(o.Id()),
packer.PackInt(o.Size()),
packer.PackInt(o.BestTime),
packer.PackInt(o.Precision),
packer.PackInt(o.RaceFlags),
)
}
func (o *GameDataRace) Unpack(u *packer.Unpacker) error {
o.BestTime = u.GetInt()
o.Precision = u.GetInt()
o.RaceFlags = u.GetInt()
return nil
}

View file

@ -19,7 +19,7 @@ func (o *GameDataTeam) Id() int {
return o.ItemId return o.ItemId
} }
func (o *GameDataTeam) Type() int { func (o *GameDataTeam) TypeId() int {
return network7.ObjGameDataTeam return network7.ObjGameDataTeam
} }
@ -29,7 +29,7 @@ func (o *GameDataTeam) Size() int {
func (o *GameDataTeam) Pack() []byte { func (o *GameDataTeam) Pack() []byte {
return slices.Concat( return slices.Concat(
packer.PackInt(o.Type()), packer.PackInt(o.TypeId()),
packer.PackInt(o.Id()), packer.PackInt(o.Id()),
packer.PackInt(o.TeamscoreRed), packer.PackInt(o.TeamscoreRed),

45
object7/hammer_hit.go Normal file
View file

@ -0,0 +1,45 @@
package object7
import (
"reflect"
"slices"
"github.com/teeworlds-go/go-teeworlds-protocol/network7"
"github.com/teeworlds-go/go-teeworlds-protocol/packer"
)
type HammerHit struct {
ItemId int
X int
Y int
}
func (o *HammerHit) Id() int {
return o.ItemId
}
func (o *HammerHit) TypeId() int {
return network7.ObjHammerHit
}
func (o *HammerHit) Size() int {
return reflect.TypeOf(HammerHit{}).NumField() - 1
}
func (o *HammerHit) Pack() []byte {
return slices.Concat(
packer.PackInt(o.TypeId()),
packer.PackInt(o.Id()),
packer.PackInt(o.X),
packer.PackInt(o.Y),
)
}
func (o *HammerHit) Unpack(u *packer.Unpacker) error {
o.X = u.GetInt()
o.Y = u.GetInt()
return nil
}

54
object7/laser.go Normal file
View file

@ -0,0 +1,54 @@
package object7
import (
"reflect"
"slices"
"github.com/teeworlds-go/go-teeworlds-protocol/network7"
"github.com/teeworlds-go/go-teeworlds-protocol/packer"
)
type Laser struct {
ItemId int
X int
Y int
FromX int
FromY int
StartTick int
}
func (o *Laser) Id() int {
return o.ItemId
}
func (o *Laser) TypeId() int {
return network7.ObjLaser
}
func (o *Laser) Size() int {
return reflect.TypeOf(Laser{}).NumField() - 1
}
func (o *Laser) Pack() []byte {
return slices.Concat(
packer.PackInt(o.TypeId()),
packer.PackInt(o.Id()),
packer.PackInt(o.X),
packer.PackInt(o.Y),
packer.PackInt(o.FromX),
packer.PackInt(o.FromY),
packer.PackInt(o.StartTick),
)
}
func (o *Laser) Unpack(u *packer.Unpacker) error {
o.X = u.GetInt()
o.Y = u.GetInt()
o.FromX = u.GetInt()
o.FromY = u.GetInt()
o.StartTick = u.GetInt()
return nil
}

View file

@ -11,16 +11,16 @@ import (
type Pickup struct { type Pickup struct {
ItemId int ItemId int
X int X int
Y int Y int
PickupType network7.Pickup Type network7.Pickup
} }
func (o *Pickup) Id() int { func (o *Pickup) Id() int {
return o.ItemId return o.ItemId
} }
func (o *Pickup) Type() int { func (o *Pickup) TypeId() int {
return network7.ObjPickup return network7.ObjPickup
} }
@ -30,19 +30,19 @@ func (o *Pickup) Size() int {
func (o *Pickup) Pack() []byte { func (o *Pickup) Pack() []byte {
return slices.Concat( return slices.Concat(
packer.PackInt(o.Type()), packer.PackInt(o.TypeId()),
packer.PackInt(o.Id()), packer.PackInt(o.Id()),
packer.PackInt(o.X), packer.PackInt(o.X),
packer.PackInt(o.Y), packer.PackInt(o.Y),
packer.PackInt(int(o.PickupType)), packer.PackInt(int(o.Type)),
) )
} }
func (o *Pickup) Unpack(u *packer.Unpacker) error { func (o *Pickup) Unpack(u *packer.Unpacker) error {
o.X = u.GetInt() o.X = u.GetInt()
o.Y = u.GetInt() o.Y = u.GetInt()
o.PickupType = network7.Pickup(u.GetInt()) o.Type = network7.Pickup(u.GetInt())
return nil return nil
} }

View file

@ -21,7 +21,7 @@ func (o *PlayerInfo) Id() int {
return o.ItemId return o.ItemId
} }
func (o *PlayerInfo) Type() int { func (o *PlayerInfo) TypeId() int {
return network7.ObjPlayerInfo return network7.ObjPlayerInfo
} }
@ -31,7 +31,7 @@ func (o *PlayerInfo) Size() int {
func (o *PlayerInfo) Pack() []byte { func (o *PlayerInfo) Pack() []byte {
return slices.Concat( return slices.Concat(
packer.PackInt(o.Type()), packer.PackInt(o.TypeId()),
packer.PackInt(o.Id()), packer.PackInt(o.Id()),
packer.PackInt(o.PlayerFlags), packer.PackInt(o.PlayerFlags),

View file

@ -0,0 +1,50 @@
package object7
import (
"reflect"
"slices"
"github.com/teeworlds-go/go-teeworlds-protocol/network7"
"github.com/teeworlds-go/go-teeworlds-protocol/packer"
)
// this is a new snap item that was added after the 0.7 release
// so for backwards compability it includes a size field
// and older clients ignore it
//
// this message is not used by official servers
// and is part of an effort to support community made race modifications
type PlayerInfoRace struct {
ItemId int
RaceStartTick int
}
func (o *PlayerInfoRace) Id() int {
return o.ItemId
}
func (o *PlayerInfoRace) TypeId() int {
return network7.ObjPlayerInfoRace
}
func (o *PlayerInfoRace) Size() int {
// TODO: is this correct? is this just payload size or does it contain the size field as well?
return reflect.TypeOf(PlayerInfoRace{}).NumField() - 1
}
func (o *PlayerInfoRace) Pack() []byte {
return slices.Concat(
packer.PackInt(o.TypeId()),
packer.PackInt(o.Id()),
packer.PackInt(o.Size()),
packer.PackInt(o.RaceStartTick),
)
}
func (o *PlayerInfoRace) Unpack(u *packer.Unpacker) error {
o.RaceStartTick = u.GetInt()
return nil
}

73
object7/player_input.go Normal file
View file

@ -0,0 +1,73 @@
package object7
import (
"reflect"
"slices"
"github.com/teeworlds-go/go-teeworlds-protocol/network7"
"github.com/teeworlds-go/go-teeworlds-protocol/packer"
)
// this object is never included in the snap
// the same order of ints is sent in the system message input
//
// but technically this item is unused
type PlayerInput struct {
ItemId int
Direction int
TargetX int
TargetY int
Jump int
Fire int
Hook int
PlayerFlags int
WantedWeapon int
NextWeapon int
PrevWeapon int
}
func (o *PlayerInput) Id() int {
return o.ItemId
}
func (o *PlayerInput) TypeId() int {
return network7.ObjPlayerInput
}
func (o *PlayerInput) Size() int {
return reflect.TypeOf(PlayerInput{}).NumField() - 1
}
func (o *PlayerInput) Pack() []byte {
return slices.Concat(
packer.PackInt(o.TypeId()),
packer.PackInt(o.Id()),
packer.PackInt(o.Direction),
packer.PackInt(o.TargetX),
packer.PackInt(o.TargetY),
packer.PackInt(o.Jump),
packer.PackInt(o.Fire),
packer.PackInt(o.Hook),
packer.PackInt(o.PlayerFlags),
packer.PackInt(o.WantedWeapon),
packer.PackInt(o.NextWeapon),
packer.PackInt(o.PrevWeapon),
)
}
func (o *PlayerInput) Unpack(u *packer.Unpacker) error {
o.Direction = u.GetInt()
o.TargetX = u.GetInt()
o.TargetY = u.GetInt()
o.Jump = u.GetInt()
o.Fire = u.GetInt()
o.Hook = u.GetInt()
o.PlayerFlags = u.GetInt()
o.WantedWeapon = u.GetInt()
o.NextWeapon = u.GetInt()
o.PrevWeapon = u.GetInt()
return nil
}

57
object7/projectile.go Normal file
View file

@ -0,0 +1,57 @@
package object7
import (
"reflect"
"slices"
"github.com/teeworlds-go/go-teeworlds-protocol/network7"
"github.com/teeworlds-go/go-teeworlds-protocol/packer"
)
type Projectile struct {
ItemId int
X int
Y int
VelX int
VelY int
Type int
StartTick int
}
func (o *Projectile) Id() int {
return o.ItemId
}
func (o *Projectile) TypeId() int {
return network7.ObjProjectile
}
func (o *Projectile) Size() int {
return reflect.TypeOf(Projectile{}).NumField() - 1
}
func (o *Projectile) Pack() []byte {
return slices.Concat(
packer.PackInt(o.TypeId()),
packer.PackInt(o.Id()),
packer.PackInt(o.X),
packer.PackInt(o.Y),
packer.PackInt(o.VelX),
packer.PackInt(o.VelY),
packer.PackInt(o.Type),
packer.PackInt(o.StartTick),
)
}
func (o *Projectile) Unpack(u *packer.Unpacker) error {
o.X = u.GetInt()
o.Y = u.GetInt()
o.VelX = u.GetInt()
o.VelY = u.GetInt()
o.Type = u.GetInt()
o.StartTick = u.GetInt()
return nil
}

View file

@ -8,8 +8,13 @@ import (
) )
type SnapObject interface { type SnapObject interface {
// id separating this snap item from other items with same type
// the ids are unique per type
// for players it matches their ClientId
Id() int Id() int
Type() int
// type of the snap item
TypeId() int
// number of packed integers // number of packed integers
// not the number of bytes // not the number of bytes
@ -21,29 +26,47 @@ type SnapObject interface {
// Comes without payload // Comes without payload
// you have to call item.Unpack(u) manually after getting it // you have to call item.Unpack(u) manually after getting it
func NewObject(itemType int, itemId int) SnapObject { func NewObject(typeId int, itemId int) SnapObject {
if itemType == network7.ObjFlag { if typeId == network7.ObjProjectile {
return &Projectile{ItemId: itemId}
} else if typeId == network7.ObjLaser {
return &Laser{ItemId: itemId}
} else if typeId == network7.ObjFlag {
return &Flag{ItemId: itemId} return &Flag{ItemId: itemId}
} else if itemType == network7.ObjPickup { } else if typeId == network7.ObjPickup {
return &Pickup{ItemId: itemId} return &Pickup{ItemId: itemId}
} else if itemType == network7.ObjGameData { } else if typeId == network7.ObjGameData {
return &GameData{ItemId: itemId} return &GameData{ItemId: itemId}
} else if itemType == network7.ObjGameDataTeam { } else if typeId == network7.ObjGameDataTeam {
return &GameDataTeam{ItemId: itemId} return &GameDataTeam{ItemId: itemId}
} else if itemType == network7.ObjGameDataFlag { } else if typeId == network7.ObjGameDataFlag {
return &GameDataFlag{ItemId: itemId} return &GameDataFlag{ItemId: itemId}
} else if itemType == network7.ObjCharacter { } else if typeId == network7.ObjCharacter {
return &Character{ItemId: itemId} return &Character{ItemId: itemId}
} else if itemType == network7.ObjPlayerInfo { } else if typeId == network7.ObjPlayerInfo {
return &PlayerInfo{ItemId: itemId} return &PlayerInfo{ItemId: itemId}
} else if typeId == network7.ObjSpectatorInfo {
return &SpectatorInfo{ItemId: itemId}
} else if typeId == network7.ObjExplosion {
return &Explosion{ItemId: itemId}
} else if typeId == network7.ObjSpawn {
return &Spawn{ItemId: itemId}
} else if typeId == network7.ObjHammerHit {
return &HammerHit{ItemId: itemId}
} else if typeId == network7.ObjDeath {
return &Death{ItemId: itemId}
} else if typeId == network7.ObjSoundWorld {
return &SoundWorld{ItemId: itemId}
} else if typeId == network7.ObjDamage {
return &Damage{ItemId: itemId}
} }
// TODO: remove this is just for debugging // TODO: remove this is just for debugging
log.Fatalf("unknown item type %d\n", itemType) log.Fatalf("unknown item type %d\n", typeId)
unknown := &Unknown{ unknown := &Unknown{
ItemId: itemId, ItemId: itemId,
ItemType: itemType, ItemType: typeId,
} }
return unknown return unknown
} }

48
object7/sound_world.go Normal file
View file

@ -0,0 +1,48 @@
package object7
import (
"reflect"
"slices"
"github.com/teeworlds-go/go-teeworlds-protocol/network7"
"github.com/teeworlds-go/go-teeworlds-protocol/packer"
)
type SoundWorld struct {
ItemId int
X int
Y int
SoundId int
}
func (o *SoundWorld) Id() int {
return o.ItemId
}
func (o *SoundWorld) TypeId() int {
return network7.ObjSoundWorld
}
func (o *SoundWorld) Size() int {
return reflect.TypeOf(SoundWorld{}).NumField() - 1
}
func (o *SoundWorld) Pack() []byte {
return slices.Concat(
packer.PackInt(o.TypeId()),
packer.PackInt(o.Id()),
packer.PackInt(o.X),
packer.PackInt(o.Y),
packer.PackInt(o.SoundId),
)
}
func (o *SoundWorld) Unpack(u *packer.Unpacker) error {
o.X = u.GetInt()
o.Y = u.GetInt()
o.SoundId = u.GetInt()
return nil
}

45
object7/spawn.go Normal file
View file

@ -0,0 +1,45 @@
package object7
import (
"reflect"
"slices"
"github.com/teeworlds-go/go-teeworlds-protocol/network7"
"github.com/teeworlds-go/go-teeworlds-protocol/packer"
)
type Spawn struct {
ItemId int
X int
Y int
}
func (o *Spawn) Id() int {
return o.ItemId
}
func (o *Spawn) TypeId() int {
return network7.ObjSpawn
}
func (o *Spawn) Size() int {
return reflect.TypeOf(Spawn{}).NumField() - 1
}
func (o *Spawn) Pack() []byte {
return slices.Concat(
packer.PackInt(o.TypeId()),
packer.PackInt(o.Id()),
packer.PackInt(o.X),
packer.PackInt(o.Y),
)
}
func (o *Spawn) Unpack(u *packer.Unpacker) error {
o.X = u.GetInt()
o.Y = u.GetInt()
return nil
}

51
object7/spectator_info.go Normal file
View file

@ -0,0 +1,51 @@
package object7
import (
"reflect"
"slices"
"github.com/teeworlds-go/go-teeworlds-protocol/network7"
"github.com/teeworlds-go/go-teeworlds-protocol/packer"
)
type SpectatorInfo struct {
ItemId int
SpecMode network7.Spec
SpectatorID int
X int
Y int
}
func (o *SpectatorInfo) Id() int {
return o.ItemId
}
func (o *SpectatorInfo) TypeId() int {
return network7.ObjSpectatorInfo
}
func (o *SpectatorInfo) Size() int {
return reflect.TypeOf(SpectatorInfo{}).NumField() - 1
}
func (o *SpectatorInfo) Pack() []byte {
return slices.Concat(
packer.PackInt(o.TypeId()),
packer.PackInt(o.Id()),
packer.PackInt(int(o.SpecMode)),
packer.PackInt(o.SpectatorID),
packer.PackInt(o.X),
packer.PackInt(o.Y),
)
}
func (o *SpectatorInfo) Unpack(u *packer.Unpacker) error {
o.SpecMode = network7.Spec(u.GetInt())
o.SpectatorID = u.GetInt()
o.X = u.GetInt()
o.Y = u.GetInt()
return nil
}

View file

@ -23,7 +23,7 @@ func (o *Unknown) Id() int {
return o.ItemId return o.ItemId
} }
func (o *Unknown) Type() int { func (o *Unknown) TypeId() int {
return o.ItemType return o.ItemType
} }
@ -33,7 +33,7 @@ func (o *Unknown) Size() int {
func (o *Unknown) Pack() []byte { func (o *Unknown) Pack() []byte {
data := slices.Concat( data := slices.Concat(
packer.PackInt(o.Type()), packer.PackInt(o.TypeId()),
packer.PackInt(o.Id()), packer.PackInt(o.Id()),
packer.PackInt(o.Size()), packer.PackInt(o.Size()),
) )