Skip to content

Commit

Permalink
Find own tee in snapshot and basic controls
Browse files Browse the repository at this point in the history
  • Loading branch information
ChillerDragon committed Jul 5, 2024
1 parent ba1e1e8 commit 98701db
Show file tree
Hide file tree
Showing 9 changed files with 139 additions and 12 deletions.
45 changes: 44 additions & 1 deletion snapshot7/storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import (
"fmt"
"slices"
"sort"

"github.com/teeworlds-go/protocol/object7"
)

const (
Expand All @@ -16,6 +18,7 @@ const (
// can we just put the snap as is in the map?
type holder struct {
snap *Snapshot
tick int
}

// TODO: do we need this at all?
Expand All @@ -24,8 +27,22 @@ type holder struct {
// data structure
// but in golang users could just define their own map
type Storage struct {
holder map[int]*holder
// a backlog of a few snapshots
// kept to unpack new deltas sent by the server
holder map[int]*holder

// the alt snap is the snapshot
// that should be used for everything gameplay related
// it is the snapshot of the current predicton tick
// and invalid items were already filtered out
// TODO: add prediction ticks and item validation
altSnap holder

// oldest tick still in the holder
// not the oldest tick we ever received
OldestTick int

// newest tick in the holder
NewestTick int

// use to store and concatinate data
Expand All @@ -42,6 +59,32 @@ func NewStorage() *Storage {
s.multiPartIncomingData = make([]byte, 0, MaxSize)
return s
}

func (s *Storage) AltSnap() (*Snapshot, error) {
if s.altSnap.snap == nil {
return nil, errors.New("there is no alt snap in the storage")
}
return s.altSnap.snap, nil
}

func (s *Storage) SetAltSnap(tick int, snap *Snapshot) {
s.altSnap.snap = snap
s.altSnap.tick = tick
}

func (s *Storage) FindAltSnapItem(typeId int, itemId int) (object7.SnapObject, error) {
altSnap, err := s.AltSnap()
if err != nil {
return nil, err
}
key := (typeId << 16) | (itemId & 0xffff)
item := altSnap.GetItemAtKey(key)
if item == nil {
return nil, errors.New("item not found")
}
return *item, nil
}

func (s *Storage) AddIncomingData(part int, numParts int, data []byte) error {
if part == 0 {
// reset length if we get a new snapshot
Expand Down
2 changes: 2 additions & 0 deletions teeworlds7/callbacks.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ func userMsgCallback[T any](userCallbacks []func(T, DefaultAction), msg T, defau
// // key is the network7.MessageId
// UserMsgCallbacks map[int]UserMsgCallback
type UserMsgCallbacks struct {
Tick []func(DefaultAction)

// return false to drop the packet
PacketIn []func(*protocol7.Packet) bool

Expand Down
69 changes: 66 additions & 3 deletions teeworlds7/client.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
package teeworlds7

import (
"errors"
"log"
"net"
"time"

"github.com/teeworlds-go/protocol/messages7"
"github.com/teeworlds-go/protocol/network7"
"github.com/teeworlds-go/protocol/object7"
"github.com/teeworlds-go/protocol/protocol7"
"github.com/teeworlds-go/protocol/snapshot7"
)
Expand All @@ -14,9 +18,10 @@ type Player struct {
}

type Game struct {
Players []Player
Snap *GameSnap
Input *messages7.Input
Players []Player
Snap *GameSnap
Input *messages7.Input
LastSentInput messages7.Input
}

type Client struct {
Expand All @@ -34,6 +39,11 @@ type Client struct {
// udp connection
Conn net.Conn

// when the last packet was sent
// tracked to know when to send keepalives
LastSend time.Time
LastInputSend time.Time

// teeworlds session
Session protocol7.Session

Expand All @@ -42,16 +52,69 @@ type Client struct {

// teeworlds game state
Game Game

// might be -1 if we do not know our own id yet
LocalClientId int
}

// TODO: add this for all items and move it to a different file
func (client *Client) SnapFindCharacter(ClientId int) (*object7.Character, error) {
item, err := client.SnapshotStorage.FindAltSnapItem(network7.ObjCharacter, ClientId)
if err != nil {
return nil, err
}
character, ok := item.(*object7.Character)
if ok == false {
return nil, errors.New("failed to cast character")
}
return character, nil
}

func NewClient() *Client {
client := &Client{}
client.SnapshotStorage = snapshot7.NewStorage()
client.Game.Snap = &GameSnap{}
client.Game.Input = &messages7.Input{}
client.LocalClientId = -1
client.LastSend = time.Now()
return client
}

func (client *Client) sendInputIfNeeded() bool {
diff := time.Now().Sub(client.LastSend)
send := false
// at least every 10hz or on change
if diff.Microseconds() > 1000000 {
send = true
}
if client.Game.LastSentInput != *client.Game.Input {
send = true
}

if send {
client.SendInput()
}

return send
}

func (client *Client) gameTick() {
defaultAction := func() {
if client.sendInputIfNeeded() == true {
return
}

diff := time.Now().Sub(client.LastSend)
if diff.Seconds() > 2 {
client.SendKeepAlive()
}
}

for _, callback := range client.Callbacks.Tick {
callback(defaultAction)
}
}

func (client *Client) throwError(err error) {
for _, callback := range client.Callbacks.InternalError {
if callback(err) == false {
Expand Down
3 changes: 3 additions & 0 deletions teeworlds7/game.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ func (client *Client) processGame(netMsg messages7.NetMessage, response *protoco
case *messages7.SvClientInfo:
userMsgCallback(client.Callbacks.GameSvClientInfo, msg, func() {
client.Game.Players[msg.ClientId].Info = *msg
if msg.Local {
client.LocalClientId = msg.ClientId
}
fmt.Printf("got client info id=%d name=%s\n", msg.ClientId, msg.Name)
})
case *messages7.SvReadyToEnter:
Expand Down
2 changes: 1 addition & 1 deletion teeworlds7/networking.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ func (client *Client) Connect(serverIp string, serverPort int) {
client.throwError(err)
}
default:
// do nothing
client.gameTick()
}
}
}
3 changes: 3 additions & 0 deletions teeworlds7/send_message_hooks.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package teeworlds7

import (
"fmt"
"time"

"github.com/teeworlds-go/protocol/messages7"
)
Expand Down Expand Up @@ -122,6 +123,8 @@ func (client *Client) registerMessagesCallbacks(messages []messages7.NetMessage)
if userSendMsgCallback(client.Callbacks.SysInputOut, castMsg) == false {
continue
}
client.Game.LastSentInput = *castMsg
client.LastInputSend = time.Now()
case *messages7.RconCmd:
if userSendMsgCallback(client.Callbacks.SysRconCmdOut, castMsg) == false {
continue
Expand Down
3 changes: 3 additions & 0 deletions teeworlds7/system.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ func (client *Client) processSystem(netMsg messages7.NetMessage, response *proto
client.Game.Input.AckGameTick = msg.GameTick
client.Game.Input.PredictionTick = client.SnapshotStorage.NewestTick
client.Game.Snap.fill(newFullSnap)
client.SnapshotStorage.SetAltSnap(msg.GameTick, newFullSnap)

response.Messages = append(response.Messages, client.Game.Input)
})
Expand Down Expand Up @@ -149,7 +150,9 @@ func (client *Client) processSystem(netMsg messages7.NetMessage, response *proto
client.Game.Input.AckGameTick = msg.GameTick
client.Game.Input.PredictionTick = client.SnapshotStorage.NewestTick
client.Game.Snap.fill(newFullSnap)
client.SnapshotStorage.SetAltSnap(msg.GameTick, newFullSnap)

client.SendInput()
response.Messages = append(response.Messages, client.Game.Input)
})
case *messages7.SnapEmpty:
Expand Down
20 changes: 13 additions & 7 deletions teeworlds7/user_actions.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package teeworlds7

import (
"fmt"
"time"

"github.com/teeworlds-go/protocol/messages7"
"github.com/teeworlds-go/protocol/network7"
Expand Down Expand Up @@ -80,6 +81,7 @@ func (client *Client) SendPacket(packet *protocol7.Packet) error {
}
}

client.LastSend = time.Now()
client.Conn.Write(packet.Pack(&client.Session))
return nil
}
Expand Down Expand Up @@ -128,39 +130,39 @@ func (client *Client) SendInput() {

func (client *Client) Right() {
client.Game.Input.Direction = 1
client.SendInput()
// client.SendInput()
}

func (client *Client) Left() {
client.Game.Input.Direction = -1
client.SendInput()
// client.SendInput()
}

func (client *Client) Stop() {
client.Game.Input.Direction = 0
client.SendInput()
// client.SendInput()
}

func (client *Client) Jump() {
client.Game.Input.Jump = 1
client.SendInput()
// client.SendInput()
}

func (client *Client) Hook() {
client.Game.Input.Hook = 1
client.SendInput()
// client.SendInput()
}

func (client *Client) Fire() {
// TODO: fire is weird do we ever have to reset or mask it or something?
client.Game.Input.Fire++
client.SendInput()
// client.SendInput()
}

func (client *Client) Aim(x int, y int) {
client.Game.Input.TargetX = x
client.Game.Input.TargetY = y
client.SendInput()
// client.SendInput()
}

// see also SendWhisper()
Expand Down Expand Up @@ -198,3 +200,7 @@ func (client *Client) SendWhisper(targetId int, msg string) {
},
)
}

func (client *Client) SendKeepAlive() {
client.SendMessage(&messages7.CtrlKeepAlive{})
}
4 changes: 4 additions & 0 deletions teeworlds7/user_hooks.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ import (
// special cases
// --------------------------------

func (client *Client) OnTick(callback func(defaultAction DefaultAction)) {
client.Callbacks.Tick = append(client.Callbacks.Tick, callback)
}

// if not implemented by the user the application might throw and exit
//
// return false to drop the error
Expand Down

0 comments on commit 98701db

Please sign in to comment.