|
|
|
@ -1,29 +1,69 @@
|
|
|
|
|
package game |
|
|
|
|
|
|
|
|
|
import ( |
|
|
|
|
"crypto/sha1" |
|
|
|
|
"image/color" |
|
|
|
|
"log" |
|
|
|
|
"os" |
|
|
|
|
"strconv" |
|
|
|
|
"strings" |
|
|
|
|
|
|
|
|
|
"code.rocketnine.space/tslocum/boxbrawl/entity" |
|
|
|
|
"code.rocketnine.space/tslocum/boxbrawl/system" |
|
|
|
|
"code.rocketnine.space/tslocum/boxbrawl/world" |
|
|
|
|
"code.rocketnine.space/tslocum/etk" |
|
|
|
|
"code.rocketnine.space/tslocum/gohan" |
|
|
|
|
"github.com/assemblaj/ggpo" |
|
|
|
|
"github.com/hajimehoshi/ebiten/v2" |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
type Game struct{} |
|
|
|
|
var backend ggpo.Backend |
|
|
|
|
|
|
|
|
|
func NewGame() (*Game, error) { |
|
|
|
|
g := &Game{} |
|
|
|
|
var currentPlayer = 1 |
|
|
|
|
|
|
|
|
|
entity.NewOnceEntity() |
|
|
|
|
type Game struct { |
|
|
|
|
Players []Player |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
gohan.AddSystem(&system.UISystem{}) |
|
|
|
|
var addedGame bool |
|
|
|
|
|
|
|
|
|
func NewGame() (*Game, error) { |
|
|
|
|
var player1 = Player{ |
|
|
|
|
X: 50, |
|
|
|
|
Y: 50, |
|
|
|
|
Color: color.RGBA{255, 0, 0, 255}, |
|
|
|
|
PlayerNum: 1, |
|
|
|
|
} |
|
|
|
|
var player2 = Player{ |
|
|
|
|
X: 150, |
|
|
|
|
Y: 50, |
|
|
|
|
Color: color.RGBA{0, 0, 255, 255}, |
|
|
|
|
PlayerNum: 2, |
|
|
|
|
} |
|
|
|
|
g := &Game{ |
|
|
|
|
Players: []Player{player1, player2}, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if !addedGame { |
|
|
|
|
entity.NewOnceEntity() |
|
|
|
|
gohan.AddSystem(&system.UISystem{}) |
|
|
|
|
addedGame = true |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return g, nil |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (g *Game) clone() (result *Game) { |
|
|
|
|
result = &Game{} |
|
|
|
|
*result = *g |
|
|
|
|
|
|
|
|
|
result.Players = make([]Player, len(g.Players)) |
|
|
|
|
for i := range g.Players { |
|
|
|
|
result.Players[i] = g.Players[i].clone() |
|
|
|
|
} |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (g *Game) Layout(outsideWidth, outsideHeight int) (screenWidth, screenHeight int) { |
|
|
|
|
if outsideWidth != world.ScreenWidth || outsideHeight != world.ScreenHeight { |
|
|
|
|
if world.ScreenWidth != 0 || world.ScreenHeight != 0 { |
|
|
|
@ -35,11 +75,71 @@ func (g *Game) Layout(outsideWidth, outsideHeight int) (screenWidth, screenHeigh
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (g *Game) Update() error { |
|
|
|
|
if ebiten.IsWindowBeingClosed() { |
|
|
|
|
if ebiten.IsWindowBeingClosed() || (!world.WASM && ebiten.IsKeyPressed(ebiten.KeyEscape)) { |
|
|
|
|
g.Exit() |
|
|
|
|
return nil |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
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) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
log.Println("start networking") |
|
|
|
|
|
|
|
|
|
localPort := world.LocalPort |
|
|
|
|
if localPort == 0 { |
|
|
|
|
localPort = p |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
numPlayers := 2 |
|
|
|
|
playerSize := 20 |
|
|
|
|
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.NewRemotePlayer(playerSize, 1, address, p) |
|
|
|
|
players[1] = ggpo.NewLocalPlayer(playerSize, 2) |
|
|
|
|
currentPlayer = 2 |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if world.LocalPort != 0 { |
|
|
|
|
log.Printf("Client port for connection: %d", world.LocalPort) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
l := p |
|
|
|
|
if !world.ConnectPromptHost { |
|
|
|
|
l = localPort |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
g.InitNetworking(l, numPlayers, players, 0) |
|
|
|
|
|
|
|
|
|
world.ConnectPromptActive = true |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if world.ConnectPromptActive { |
|
|
|
|
err := backend.Idle(0) // TODO Why 0?
|
|
|
|
|
if err != nil { |
|
|
|
|
panic(err) |
|
|
|
|
} |
|
|
|
|
g.RunFrame() |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return gohan.Update() |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -53,3 +153,152 @@ func (g *Game) Draw(screen *ebiten.Image) {
|
|
|
|
|
func (g *Game) Exit() { |
|
|
|
|
os.Exit(0) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (g *Game) InitNetworking(localPort int, numPlayers int, players []ggpo.Player, numSpectators int) { |
|
|
|
|
var result error |
|
|
|
|
var inputBits InputBits = 0 |
|
|
|
|
var inputSize int = len(encodeInputs(inputBits)) |
|
|
|
|
|
|
|
|
|
session := NewGameSession() |
|
|
|
|
|
|
|
|
|
peer := ggpo.NewPeer(session, localPort, numPlayers, inputSize) |
|
|
|
|
//peer := ggpo.NewSyncTest(&session, numPlayers, 8, inputSize, true)
|
|
|
|
|
backend = &peer |
|
|
|
|
session.backend = backend |
|
|
|
|
peer.InitializeConnection() |
|
|
|
|
peer.Start() |
|
|
|
|
|
|
|
|
|
//session.SetDisconnectTimeout(3000)
|
|
|
|
|
//session.SetDisconnectNotifyStart(1000)
|
|
|
|
|
|
|
|
|
|
for i := 0; i < numPlayers+numSpectators; i++ { |
|
|
|
|
var handle ggpo.PlayerHandle |
|
|
|
|
result = peer.AddPlayer(&players[i], &handle) |
|
|
|
|
if players[i].PlayerType == ggpo.PlayerTypeLocal { |
|
|
|
|
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.SetDisconnectTimeout(3000) |
|
|
|
|
peer.SetDisconnectNotifyStart(1000) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (g *Game) RunFrame() { |
|
|
|
|
input := g.ReadInputs() |
|
|
|
|
buffer := encodeInputs(input) |
|
|
|
|
|
|
|
|
|
//fmt.Println("Attempting to add local inputs")
|
|
|
|
|
result := backend.AddLocalInput(ggpo.PlayerHandle(currentPlayer), buffer, len(buffer)) |
|
|
|
|
|
|
|
|
|
//fmt.Println("Attempt to add local inputs complete")
|
|
|
|
|
if result == nil { |
|
|
|
|
//fmt.Println("Attempt to add local inputs was successful")
|
|
|
|
|
var values [][]byte |
|
|
|
|
disconnectFlags := 0 |
|
|
|
|
|
|
|
|
|
//fmt.Println("Attempting to synchronize inputs")
|
|
|
|
|
values, result = backend.SyncInput(&disconnectFlags) |
|
|
|
|
if result == nil { |
|
|
|
|
//fmt.Println("Attempt synchronize inputs was sucessful")
|
|
|
|
|
inputs := decodeInputs(values) |
|
|
|
|
//fmt.Println("Advancing Frame from game loop")
|
|
|
|
|
g.AdvanceFrame(inputs, disconnectFlags) |
|
|
|
|
} else { |
|
|
|
|
//fmt.Printf("Attempt synchronize inputs was unsuccessful: %s\n", result)
|
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
//fmt.Printf("Attempt to add local inputs unsuccessful: %s\n", result)
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (g *Game) AdvanceFrame(inputs []InputBits, disconnectFlags int) { |
|
|
|
|
log.Println("ADVANCE FRAME") |
|
|
|
|
|
|
|
|
|
g.UpdateByInputs(inputs) |
|
|
|
|
err := backend.AdvanceFrame(uint32(g.Checksum())) |
|
|
|
|
if err != nil { |
|
|
|
|
panic(err) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (g *Game) UpdateByInputs(inputs []InputBits) { |
|
|
|
|
for i, input := range inputs { |
|
|
|
|
if input.isButtonOn(int(ebiten.KeyArrowUp)) { |
|
|
|
|
g.Players[i].Y-- |
|
|
|
|
} |
|
|
|
|
if input.isButtonOn(int(ebiten.KeyArrowDown)) { |
|
|
|
|
g.Players[i].Y++ |
|
|
|
|
} |
|
|
|
|
if input.isButtonOn(int(ebiten.KeyArrowLeft)) { |
|
|
|
|
g.Players[i].X-- |
|
|
|
|
} |
|
|
|
|
if input.isButtonOn(int(ebiten.KeyArrowRight)) { |
|
|
|
|
g.Players[i].X++ |
|
|
|
|
} |
|
|
|
|
if input.isButtonOn(int(ebiten.KeyW)) { |
|
|
|
|
g.Players[i].Y-- |
|
|
|
|
} |
|
|
|
|
if input.isButtonOn(int(ebiten.KeyS)) { |
|
|
|
|
g.Players[i].Y++ |
|
|
|
|
} |
|
|
|
|
if input.isButtonOn(int(ebiten.KeyA)) { |
|
|
|
|
g.Players[i].X-- |
|
|
|
|
} |
|
|
|
|
if input.isButtonOn(int(ebiten.KeyD)) { |
|
|
|
|
g.Players[i].X++ |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (g *Game) ReadInputs() InputBits { |
|
|
|
|
var in InputBits |
|
|
|
|
|
|
|
|
|
if ebiten.IsKeyPressed(ebiten.KeyArrowUp) { |
|
|
|
|
in.setButton(int(ebiten.KeyArrowUp)) |
|
|
|
|
} |
|
|
|
|
if ebiten.IsKeyPressed(ebiten.KeyArrowDown) { |
|
|
|
|
in.setButton(int(ebiten.KeyArrowDown)) |
|
|
|
|
} |
|
|
|
|
if ebiten.IsKeyPressed(ebiten.KeyArrowLeft) { |
|
|
|
|
in.setButton(int(ebiten.KeyArrowLeft)) |
|
|
|
|
} |
|
|
|
|
if ebiten.IsKeyPressed(ebiten.KeyArrowRight) { |
|
|
|
|
in.setButton(int(ebiten.KeyArrowRight)) |
|
|
|
|
} |
|
|
|
|
return in |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (g *Game) ReadInputsP2() InputBits { |
|
|
|
|
var in InputBits |
|
|
|
|
|
|
|
|
|
if ebiten.IsKeyPressed(ebiten.KeyW) { |
|
|
|
|
in.setButton(int(ebiten.KeyW)) |
|
|
|
|
} |
|
|
|
|
if ebiten.IsKeyPressed(ebiten.KeyS) { |
|
|
|
|
in.setButton(int(ebiten.KeyS)) |
|
|
|
|
} |
|
|
|
|
if ebiten.IsKeyPressed(ebiten.KeyA) { |
|
|
|
|
in.setButton(int(ebiten.KeyA)) |
|
|
|
|
} |
|
|
|
|
if ebiten.IsKeyPressed(ebiten.KeyD) { |
|
|
|
|
in.setButton(int(ebiten.KeyD)) |
|
|
|
|
} |
|
|
|
|
return in |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (g *Game) Checksum() int { |
|
|
|
|
h := sha1.New() |
|
|
|
|
h.Write([]byte(g.String())) |
|
|
|
|
toSum := h.Sum(nil) |
|
|
|
|
sum := 0 |
|
|
|
|
for _, v := range toSum { |
|
|
|
|
sum += int(v) |
|
|
|
|
} |
|
|
|
|
return sum |
|
|
|
|
} |
|
|
|
|