Browse Source

Add frame data system

wip
Trevor Slocum 3 weeks ago
parent
commit
b340d821f7
  1. 82
      component/player.go
  2. 1
      flags.go
  3. 181
      game/game.go
  4. 14
      game/session.go
  5. 6
      go.mod
  6. 12
      go.sum
  7. 3
      main.go
  8. 7
      system/player.go
  9. 106
      system/ui.go
  10. 17
      world/world.go

82
component/player.go

@ -2,6 +2,7 @@ package component
import (
"fmt"
"image"
"image/color"
)
@ -12,6 +13,74 @@ const (
ActionPunch
)
type HitboxType int
const (
HitboxInvalid HitboxType = iota
HitboxNormal
HitboxHurt
)
type FrameData struct {
T HitboxType
R image.Rectangle
}
const playerSize = 20
// AllPlayerFrames defines all frame data for the game. Frames are defined in reverse order.
var AllPlayerFrames = [][][]FrameData{
{ // ActionIdle
{
{
T: HitboxNormal,
R: image.Rect(0, 0, playerSize, playerSize),
},
},
}, { // ActionPunch
{
{
T: HitboxNormal,
R: image.Rect(0, 0, playerSize, playerSize),
},
{
T: HitboxHurt,
R: image.Rect(0, 0, playerSize, playerSize),
},
},
{
{
T: HitboxNormal,
R: image.Rect(0, 0, playerSize, playerSize),
},
},
{
{
T: HitboxNormal,
R: image.Rect(0, 0, playerSize, playerSize),
},
},
{
{
T: HitboxNormal,
R: image.Rect(0, 0, playerSize, playerSize),
},
},
{
{
T: HitboxNormal,
R: image.Rect(0, 0, playerSize, playerSize),
},
},
{
{
T: HitboxNormal,
R: image.Rect(0, 0, playerSize, playerSize),
},
},
},
}
type Player struct {
X float64
Y float64
@ -23,14 +92,17 @@ type Player struct {
}
func (p *Player) String() string {
return fmt.Sprintf("Player %d: X:%f Y:%f Color: %s", p.PlayerNum, p.X, p.Y, p.Color)
return fmt.Sprintf("Player %d: X:%f Y:%f Action: %d", p.PlayerNum, p.X, p.Y, p.Action)
}
func (p *Player) Clone() Player {
result := Player{}
result.X = p.X
result.Y = p.Y
result.Color = p.Color
result.PlayerNum = p.PlayerNum
result = *p
return result
}
func OffsetRect(r image.Rectangle, x int, y int) image.Rectangle {
r.Min.X, r.Min.Y = r.Min.X+x, r.Min.Y+y
r.Max.X, r.Max.Y = r.Max.X+x, r.Max.Y+y
return r
}

1
flags.go

@ -25,6 +25,7 @@ func parseFlags() {
flag.StringVar(&connectAddress, "connect", "", "connect to a match at specified address:port")
flag.IntVar(&world.LocalPort, "local", 0, "set local port (this is not normally required)")
flag.BoolVar(&printDebug, "debug", false, "enable printing debug messages")
flag.IntVar(&world.TPS, "tps", world.DefaultTPS, "set ticks per second (this is not normally required)")
flag.Parse()
if fullscreen {

181
game/game.go

@ -19,10 +19,6 @@ import (
"github.com/hajimehoshi/ebiten/v2/inpututil"
)
var backend ggpo.Backend
var currentPlayer = 1
type Game struct {
Players []component.Player
}
@ -30,18 +26,23 @@ type Game struct {
var addedGame bool
func NewGame() (*Game, error) {
var player1 = component.Player{
X: 50,
Y: 50,
Color: color.RGBA{255, 0, 0, 255},
PlayerNum: 1,
player1 := component.Player{
PlayerNum: 1,
Color: color.RGBA{255, 0, 0, 255},
Action: component.ActionIdle,
ActionTicksLeft: 1,
X: 50,
Y: 50,
}
var player2 = component.Player{
X: 150,
Y: 50,
Color: color.RGBA{0, 0, 255, 255},
PlayerNum: 2,
player2 := component.Player{
PlayerNum: 2,
Color: color.RGBA{0, 0, 255, 255},
Action: component.ActionIdle,
ActionTicksLeft: 1,
X: 150,
Y: 50,
}
g := &Game{
Players: []component.Player{player1, player2},
}
@ -78,68 +79,78 @@ func (g *Game) Layout(_, _ int) (screenWidth, screenHeight int) {
return world.ScreenWidth, world.ScreenHeight
}
func (g *Game) Update() error {
if ebiten.IsWindowBeingClosed() || (!world.WASM && ebiten.IsKeyPressed(ebiten.KeyEscape)) {
g.Exit()
return nil
}
func (g *Game) startLocalGame() {
log.Println("Playing against the computer")
if world.ConnectPromptConfirmed && !world.ConnectPromptActive {
address := ""
port := world.ConnectPromptText
if strings.ContainsRune(port, ':') {
split := strings.Split(port, ":")
if len(split) == 2 {
address = split[0]
port = split[1]
}
}
p, err := strconv.Atoi(port)
if err != nil {
log.Fatalf("failed to read port: %s", err)
// TODO
}
func (g *Game) startNetworkGame() {
address := ""
port := world.ConnectPromptText
if strings.ContainsRune(port, ':') {
split := strings.Split(port, ":")
if len(split) == 2 {
address = split[0]
port = split[1]
}
}
p, err := strconv.Atoi(port)
if err != nil {
log.Fatalf("failed to read port: %s", err)
}
log.Println("start networking")
localPort := world.LocalPort
if localPort == 0 {
localPort = p + 1
}
if world.LocalPort != 0 {
log.Printf("Client port for connection: %d", world.LocalPort)
}
localPort := world.LocalPort
if localPort == 0 {
localPort = p + 1
}
numPlayers := 2
playerSize := 20
numPlayers := 2
playerSize := 20
players := make([]ggpo.Player, numPlayers)
if world.ConnectPromptHost {
log.Printf("Hosting at " + address + ":" + port + "...")
players := make([]ggpo.Player, numPlayers)
if world.ConnectPromptHost {
log.Printf("Hosting at " + address + ":" + port + "...")
players[0] = ggpo.NewLocalPlayer(playerSize, 1)
players[1] = ggpo.NewRemotePlayer(playerSize, 2, "127.0.0.1", localPort)
} else {
log.Printf("Connecting to " + address + ":" + port + "...")
players[0] = ggpo.NewLocalPlayer(playerSize, 1)
players[1] = ggpo.NewRemotePlayer(playerSize, 2, "127.0.0.1", localPort)
} else {
log.Printf("Connecting to " + address + ":" + port + "...")
players[0] = ggpo.NewRemotePlayer(playerSize, 1, address, p)
players[1] = ggpo.NewLocalPlayer(playerSize, 2)
currentPlayer = 2
}
world.CurrentPlayer = 2
players[0] = ggpo.NewRemotePlayer(playerSize, 1, address, p)
players[1] = ggpo.NewLocalPlayer(playerSize, 2)
}
if world.LocalPort != 0 {
log.Printf("Client port for connection: %d", world.LocalPort)
}
l := p
if !world.ConnectPromptHost {
l = localPort
}
l := p
if !world.ConnectPromptHost {
l = localPort
}
g.InitNetworking(l, numPlayers, players, 0)
g.InitNetworking(l, numPlayers, players, 0)
world.ConnectionActive = true
}
func (g *Game) Update() error {
if ebiten.IsWindowBeingClosed() || (!world.WASM && ebiten.IsKeyPressed(ebiten.KeyEscape)) {
g.Exit()
return nil
}
if world.ConnectPromptConfirmed && !world.ConnectionActive {
if world.ConnectPromptText == "" {
g.startLocalGame()
} else {
g.startNetworkGame()
}
g.playerStateUpdated()
world.ConnectPromptActive = true
}
if world.ConnectPromptActive {
err := backend.Idle(0) // TODO Why 0?
if world.ConnectionActive {
err := world.Backend.Idle(0)
if err != nil {
panic(err)
}
@ -174,8 +185,8 @@ func (g *Game) InitNetworking(localPort int, numPlayers int, players []ggpo.Play
peer := ggpo.NewPeer(session, localPort, numPlayers, inputSize)
//peer := ggpo.NewSyncTest(&session, numPlayers, 8, inputSize, true)
backend = &peer
session.backend = backend
world.Backend = &peer
session.backend = world.Backend
peer.InitializeConnection()
peer.Start()
@ -186,13 +197,13 @@ func (g *Game) InitNetworking(localPort int, numPlayers int, players []ggpo.Play
var handle ggpo.PlayerHandle
result = peer.AddPlayer(&players[i], &handle)
if players[i].PlayerType == ggpo.PlayerTypeLocal {
currentPlayer = int(handle)
world.CurrentPlayer = int(handle)
}
if result != nil {
log.Fatalf("There's an issue from AddPlayer")
}
if players[i].PlayerType == ggpo.PlayerTypeLocal {
peer.SetFrameDelay(handle, FRAME_DELAY)
peer.SetFrameDelay(handle, frameDelay)
}
}
peer.SetDisconnectTimeout(3000)
@ -204,7 +215,7 @@ func (g *Game) RunFrame() {
buffer := encodeInputs(input)
//fmt.Println("Attempting to add local inputs")
result := backend.AddLocalInput(ggpo.PlayerHandle(currentPlayer), buffer, len(buffer))
result := world.Backend.AddLocalInput(ggpo.PlayerHandle(world.CurrentPlayer), buffer, len(buffer))
//fmt.Println("Attempt to add local inputs complete")
if result == nil {
@ -213,7 +224,7 @@ func (g *Game) RunFrame() {
disconnectFlags := 0
//fmt.Println("Attempting to synchronize inputs")
values, result = backend.SyncInput(&disconnectFlags)
values, result = world.Backend.SyncInput(&disconnectFlags)
if result == nil {
//fmt.Println("Attempt synchronize inputs was sucessful")
inputs := decodeInputs(values)
@ -225,25 +236,17 @@ func (g *Game) RunFrame() {
} else {
//fmt.Printf("Attempt to add local inputs unsuccessful: %s\n", result)
}
g.playerStateUpdated()
}
func (g *Game) AdvanceFrame(inputs []InputBits, disconnectFlags int) {
g.UpdateByInputs(inputs)
err := backend.AdvanceFrame(uint32(g.Checksum()))
err := world.Backend.AdvanceFrame(uint32(g.Checksum()))
if err != nil {
panic(err)
}
for i := range g.Players {
if g.Players[i].ActionTicksLeft != 0 {
g.Players[i].ActionTicksLeft--
if g.Players[i].ActionTicksLeft == 0 {
g.Players[i].Action = component.ActionIdle
}
}
}
g.playerStateUpdated()
}
func (g *Game) UpdateByInputs(inputs []InputBits) {
@ -264,7 +267,23 @@ func (g *Game) UpdateByInputs(inputs []InputBits) {
if g.Players[i].Action == component.ActionIdle {
if input.isButtonOn(ButtonPunch) {
g.Players[i].Action = component.ActionPunch
g.Players[i].ActionTicksLeft = 25 // TODO
g.Players[i].ActionTicksLeft = len(component.AllPlayerFrames[component.ActionPunch]) // TODO
continue
}
}
if g.Players[i].ActionTicksLeft != 0 {
g.Players[i].ActionTicksLeft--
if g.Players[i].ActionTicksLeft == 0 {
g.Players[i].Action = component.ActionIdle
g.Players[i].ActionTicksLeft = len(component.AllPlayerFrames[component.ActionIdle]) // TODO
}
// TODO Apply hitboxes
if g.Players[i].ActionTicksLeft != 0 {
//frameNum := g.Players[i].ActionTicksLeft - 1
//frame := component.AllPlayerFrames[g.Players[i].Action][frameNum]
//log.Printf("frame %+v", frame)
}
}
}

14
game/session.go

@ -12,7 +12,7 @@ import (
"github.com/assemblaj/ggpo"
)
const FRAME_DELAY int = 2
const frameDelay int = 2
type GameSession struct {
backend ggpo.Backend
@ -92,5 +92,17 @@ func (g *GameSession) OnEvent(info *ggpo.Event) {
log.Println("Connection resumed")
case ggpo.EventCodeDisconnectedFromPeer:
log.Println("Connection lost")
default:
if world.Debug == 0 {
return
}
switch info.Code {
case ggpo.EventCodeConnectedToPeer:
log.Println("Connected to peer")
case ggpo.EventCodeSynchronizingWithPeer:
log.Println("Synchronizing with peer")
case ggpo.EventCodeSynchronizedWithPeer:
log.Println("Synchronized with peer")
}
}
}

6
go.mod

@ -5,7 +5,7 @@ go 1.19
require (
code.rocketnine.space/tslocum/etk v0.0.0-20230103193701-368514415e01
code.rocketnine.space/tslocum/gohan v1.0.0
github.com/assemblaj/ggpo v0.0.0-20230105182823-b13b11d28a8e
github.com/assemblaj/ggpo v0.0.0-20230106194913-0f5ca8313c51
github.com/hajimehoshi/ebiten/v2 v2.4.15
)
@ -15,8 +15,8 @@ require (
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20221017161538-93cebf72946b // indirect
github.com/hajimehoshi/file2byteslice v1.0.0 // indirect
github.com/jezek/xgb v1.1.0 // indirect
golang.org/x/exp v0.0.0-20230105000112-eab7a2c85304 // indirect
golang.org/x/exp/shiny v0.0.0-20230105000112-eab7a2c85304 // indirect
golang.org/x/exp v0.0.0-20230108222341-4b8118a2686a // indirect
golang.org/x/exp/shiny v0.0.0-20230108222341-4b8118a2686a // indirect
golang.org/x/image v0.3.0 // indirect
golang.org/x/mobile v0.0.0-20221110043201-43a038452099 // indirect
golang.org/x/sys v0.4.0 // indirect

12
go.sum

@ -5,8 +5,8 @@ code.rocketnine.space/tslocum/gohan v1.0.0/go.mod h1:12yOt5Ygl/RVwnnZSVZRuS1W6gC
code.rocketnine.space/tslocum/messeji v1.0.2 h1:3/68FnXWaBDMhfUGb8FvNpVgAHY8DX+VL7pyA/CcY94=
code.rocketnine.space/tslocum/messeji v1.0.2/go.mod h1:bSXsyjvKhFXQ7GsUxWZdO2JX83xOT/VTqFCR04thk+c=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/assemblaj/ggpo v0.0.0-20230105182823-b13b11d28a8e h1:d/uJpkRgHj5EJ06CX0TFFQLfvAeHlfWY+xfLUBkVeus=
github.com/assemblaj/ggpo v0.0.0-20230105182823-b13b11d28a8e/go.mod h1:ZKiAYEZgxDlGHGeP/VZsv1+xIRo9kQpgUFmjP/PR0lQ=
github.com/assemblaj/ggpo v0.0.0-20230106194913-0f5ca8313c51 h1:7utlNj3OWPUxtFgav/0Eu1EXOrsNS5lrq+NWnwTRnAs=
github.com/assemblaj/ggpo v0.0.0-20230106194913-0f5ca8313c51/go.mod h1:ZKiAYEZgxDlGHGeP/VZsv1+xIRo9kQpgUFmjP/PR0lQ=
github.com/ebitengine/purego v0.0.0-20220905075623-aeed57cda744/go.mod h1:Eh8I3yvknDYZeCuXH9kRNaPuHEwvXDCk378o9xszmHg=
github.com/ebitengine/purego v0.1.1 h1:HI8nW+LniW9Yb34k34jBs8nz+PNzsw68o7JF8jWFHHE=
github.com/ebitengine/purego v0.1.1/go.mod h1:Eh8I3yvknDYZeCuXH9kRNaPuHEwvXDCk378o9xszmHg=
@ -38,10 +38,10 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4=
golang.org/x/exp v0.0.0-20230105000112-eab7a2c85304 h1:YUqj+XKtfrn3kXjFIiZ8jwKROD7ioAOOHUuo3ZZ2opc=
golang.org/x/exp v0.0.0-20230105000112-eab7a2c85304/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
golang.org/x/exp/shiny v0.0.0-20230105000112-eab7a2c85304 h1:ezMmyIKsGPwRz+IHa53wCpw87I2TremhqQ8o79ytDEk=
golang.org/x/exp/shiny v0.0.0-20230105000112-eab7a2c85304/go.mod h1:UH99kUObWAZkDnWqppdQe5ZhPYESUw8I0zVV1uWBR+0=
golang.org/x/exp v0.0.0-20230108222341-4b8118a2686a h1:tlXy25amD5A7gOfbXdqCGN5k8ESEed/Ee1E5RcrYnqU=
golang.org/x/exp v0.0.0-20230108222341-4b8118a2686a/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
golang.org/x/exp/shiny v0.0.0-20230108222341-4b8118a2686a h1:mTWP1/jZnkR5zmffmGg9HfL6w81dS/u6ZJSOa4i8ot8=
golang.org/x/exp/shiny v0.0.0-20230108222341-4b8118a2686a/go.mod h1:UH99kUObWAZkDnWqppdQe5ZhPYESUw8I0zVV1uWBR+0=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.1.0/go.mod h1:iyPr49SD/G/TBxYVB/9RRtGUT5eNbo2u4NamWeQcD5c=

3
main.go

@ -17,11 +17,12 @@ func main() {
ebiten.SetWindowSize(world.DefaultScreenWidth, world.DefaultScreenHeight)
ebiten.SetWindowClosingHandled(true)
ebiten.SetFPSMode(ebiten.FPSModeVsyncOn)
ebiten.SetTPS(world.TPS)
ebiten.SetRunnableOnUnfocused(true)
parseFlags()
ebiten.SetTPS(world.TPS)
g, err := game.NewGame()
if err != nil {
log.Fatal(err)

7
system/player.go

@ -49,6 +49,13 @@ func (s *PlayerSystem) Draw(e gohan.Entity, screen *ebiten.Image) error {
r := image.Rect(int(p.X), int(p.Y), int(p.X)+size, int(p.Y)+size)
screen.SubImage(r).(*ebiten.Image).Fill(p.Color)
// TODO animate
/*if p.ActionTicksLeft != 0 {
frameNum := p.ActionTicksLeft - 1
frame := component.AllPlayerFrames[p.Action][frameNum]
log.Printf("frame %+v", frame)
}*/
switch p.Action {
case component.ActionPunch:
ebitenutil.DebugPrintAt(screen, "PUNCH", int(p.X), int(p.Y))

106
system/ui.go

@ -2,6 +2,10 @@ package system
import (
"fmt"
"image/color"
"math"
"github.com/assemblaj/ggpo"
"code.rocketnine.space/tslocum/boxbrawl/component"
"code.rocketnine.space/tslocum/boxbrawl/world"
@ -31,6 +35,9 @@ type UISystem struct {
initialized bool
buffer *etk.Text
updateTicks int
hitboxImg *ebiten.Image
}
func (u *UISystem) initialize() {
@ -45,6 +52,8 @@ func (u *UISystem) initialize() {
etk.SetRoot(inputDemo)
etk.Layout(world.InternalScreenWidth, world.InternalScreenHeight)
u.hitboxImg = ebiten.NewImage(32, 32)
u.initialized = true
}
@ -54,7 +63,7 @@ func (u *UISystem) updateBuffer() {
if world.WASM {
prompt = append(prompt, []byte("\n\n"+uiComputerPrompt)...)
prompt = append(prompt, []byte("\n\n"+uiBrowserPrompt)...)
} else if world.ConnectPromptActive {
} else if world.ConnectionActive {
if world.ConnectPromptHost {
prompt = append(prompt, []byte("\n\n"+fmt.Sprintf(uiHostListeningPrompt, world.ConnectPromptText))...)
} else {
@ -63,7 +72,7 @@ func (u *UISystem) updateBuffer() {
} else {
promptEntered := len(world.ConnectPromptText) != 0
if promptEntered || world.ConnectPromptHost {
prompt = append(prompt, []byte("\n\n"+world.ConnectPromptText)...)
prompt = append(prompt, []byte("\n\n"+world.ConnectPromptText+"_")...)
if world.ConnectPromptHost {
prompt = append(prompt, []byte("\n\n"+uiHostInfoPrompt)...)
prompt = append(prompt, []byte("\n\n"+uiHostStartPrompt)...)
@ -91,10 +100,13 @@ func (u *UISystem) Update(e gohan.Entity) error {
return nil
}
var updated bool
if !world.WASM {
if len(world.ConnectPromptText) == 0 && ebiten.IsKeyPressed(ebiten.KeyH) {
world.ConnectPromptHost = true
u.updateBuffer()
updated = true
}
var a string
@ -137,14 +149,17 @@ func (u *UISystem) Update(e gohan.Entity) error {
if a != "" {
world.ConnectPromptText += a
u.updateBuffer()
updated = true
}
if inpututil.IsKeyJustPressed(ebiten.KeyBackspace) {
if len(world.ConnectPromptText) != 0 {
world.ConnectPromptText = world.ConnectPromptText[:len(world.ConnectPromptText)-1]
u.updateBuffer()
updated = true
} else if world.ConnectPromptHost {
world.ConnectPromptHost = false
u.updateBuffer()
updated = true
}
}
}
@ -153,6 +168,14 @@ func (u *UISystem) Update(e gohan.Entity) error {
world.ConnectPromptConfirmed = true
}
if !updated {
u.updateTicks++
if u.updateTicks == 6 {
u.updateBuffer()
u.updateTicks = 0
}
}
return etk.Update()
}
@ -161,17 +184,82 @@ func (u *UISystem) Draw(e gohan.Entity, screen *ebiten.Image) error {
u.initialize()
}
if !world.ConnectPromptVisible {
return nil
}
if world.ConnectPromptVisible {
err := etk.Draw(screen)
if err != nil {
return err
}
} else if world.Debug != 0 { // In-game and debug mode is enabled
var p *component.Player
for i := 0; i < 2; i++ {
if i == 0 {
p = &world.Player1
} else {
p = &world.Player2
}
if p.ActionTicksLeft != 0 {
frameNum := p.ActionTicksLeft - 1
allData := component.AllPlayerFrames[p.Action][frameNum]
for _, data := range allData {
fillColor := color.RGBA{0, 255, 0, 255}
switch data.T {
case component.HitboxHurt:
fillColor = color.RGBA{0, 0, 255, 255}
}
bounds := u.hitboxImg.Bounds()
if bounds.Dx() != data.R.Dx() || bounds.Dy() != data.R.Dy() {
u.hitboxImg = ebiten.NewImage(data.R.Dx(), data.R.Dy())
}
u.hitboxImg.Clear()
u.hitboxImg.Fill(fillColor)
op := &ebiten.DrawImageOptions{}
op.GeoM.Translate(p.X, p.Y)
op.ColorM.Scale(1, 1, 1, 1)
screen.DrawImage(u.hitboxImg, op)
}
}
}
err := etk.Draw(screen)
if err != nil {
return err
}
if world.Debug != 0 {
ebitenutil.DebugPrintAt(screen, fmt.Sprintf("ENT %d\nUPD %d\nDRA %d\nTPS %0.0f\nFPS %0.0f", gohan.CurrentEntities(), gohan.CurrentUpdates(), gohan.CurrentDraws(), ebiten.ActualTPS(), ebiten.ActualFPS()), 2, 0)
var ping int64
var framesBehind float64
if !world.ConnectPromptVisible {
p1Stats, err := world.Backend.GetNetworkStats(ggpo.PlayerHandle(1))
if err != nil {
return fmt.Errorf("failed to get network stats for player 1: %s", err)
}
p2Stats, err := world.Backend.GetNetworkStats(ggpo.PlayerHandle(2))
if err != nil {
return fmt.Errorf("failed to get network stats for player 2: %s", err)
}
yourStats := p1Stats
if world.CurrentPlayer == 1 {
yourStats = p2Stats
}
ping = yourStats.Network.Ping
framesBehind = math.Round(float64(yourStats.Timesync.LocalFramesBehind))
}
framesLabel := "AHEAD"
if framesBehind > 0 {
framesLabel = "BEHIND"
}
ebitenutil.DebugPrintAt(screen,
fmt.Sprintf("FRAMES %s %.0f\nPING %d\nTPS %0.0f\nFPS %0.0f",
framesLabel,
math.Abs(framesBehind),
ping,
ebiten.ActualTPS(),
ebiten.ActualFPS()),
2, 0)
}
return nil
}

17
world/world.go

@ -2,11 +2,12 @@ package world
import (
"code.rocketnine.space/tslocum/boxbrawl/component"
"github.com/assemblaj/ggpo"
)
const TPS = 60
const (
DefaultTPS = 60
DefaultScreenWidth = 1280
DefaultScreenHeight = 720
@ -14,20 +15,26 @@ const (
)
var (
TPS = DefaultTPS
ScreenWidth, ScreenHeight = 0, 0
LocalPort int
ConnectPromptVisible = true
ConnectPromptVisible = true // When false, we are connected
ConnectPromptText string
ConnectPromptHost bool
ConnectPromptConfirmed bool
ConnectPromptActive bool
ConnectionActive bool
Debug = 1
Debug = 1 // TODO
WASM bool
Player1 component.Player
Player2 component.Player
CurrentPlayer = 1
Backend ggpo.Backend
)

Loading…
Cancel
Save