|
|
|
@ -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
|
|
|
|
|
}
|
|
|
|
|