Center GUI text, improve matrix test
This commit is contained in:
parent
e7a430dbd6
commit
24cce86c0f
|
@ -0,0 +1,55 @@
|
|||
# Client
|
||||
|
||||
```
|
||||
Usage of ./netris:
|
||||
-connect string
|
||||
connect to server address or socket path
|
||||
-debug
|
||||
enable debug logging
|
||||
-debug-address string
|
||||
address to serve debug info
|
||||
-matrix string
|
||||
pre-fill matrix with pieces
|
||||
-nick string
|
||||
nickname (default "Anonymous")
|
||||
-scale int
|
||||
UI scale
|
||||
-verbose
|
||||
enable verbose logging
|
||||
```
|
||||
|
||||
### -scale
|
||||
|
||||
Defaults to 0, automatically scaling up to 3x standard size based on the
|
||||
terminal window's dimensions.
|
||||
|
||||
Specify an integer to only use that UI scale.
|
||||
|
||||
### -connect
|
||||
|
||||
A TCP address in the form of address:port or socket path may be supplied.
|
||||
|
||||
# Server
|
||||
|
||||
```
|
||||
Usage of ./netris-server:
|
||||
-debug
|
||||
enable debug logging
|
||||
-debug-address string
|
||||
address to serve debug info
|
||||
-listen-socket string
|
||||
host server on socket path
|
||||
-listen-ssh string
|
||||
host SSH server on network address
|
||||
-listen-tcp string
|
||||
host server on network address
|
||||
-netris string
|
||||
path to netris client
|
||||
-verbose
|
||||
enable verbose logging
|
||||
```
|
||||
|
||||
### -listen-ssh and -netris
|
||||
|
||||
The netris client will be launched to serve incoming SSH connections. Update
|
||||
client and server binaries together.
|
|
@ -24,6 +24,10 @@ go get -u git.sr.ht/~tslocum/netris/cmd/netris
|
|||
go get -u git.sr.ht/~tslocum/netris/cmd/netris-server
|
||||
```
|
||||
|
||||
## Configure
|
||||
|
||||
See [CONFIGURATION.md](https://man.sr.ht/~tslocum/netris/CONFIGURATION.md)
|
||||
|
||||
## Support
|
||||
|
||||
Please share suggestions/issues [here](https://todo.sr.ht/~tslocum/netris).
|
||||
|
|
|
@ -20,7 +20,11 @@ var (
|
|||
listenAddressSSH string
|
||||
netrisBinary string
|
||||
debugAddress string
|
||||
done = make(chan bool)
|
||||
|
||||
logDebug bool
|
||||
logVerbose bool
|
||||
|
||||
done = make(chan bool)
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -34,7 +38,9 @@ func init() {
|
|||
flag.StringVar(&listenAddressSocket, "listen-socket", "", "host server on socket path")
|
||||
flag.StringVar(&listenAddressSSH, "listen-ssh", "", "host SSH server on network address")
|
||||
flag.StringVar(&netrisBinary, "netris", "", "path to netris client")
|
||||
flag.StringVar(&debugAddress, "debug", "", "address to serve debug info")
|
||||
flag.StringVar(&debugAddress, "debug-address", "", "address to serve debug info")
|
||||
flag.BoolVar(&logDebug, "debug", false, "enable debug logging")
|
||||
flag.BoolVar(&logVerbose, "verbose", false, "enable verbose logging")
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"fmt"
|
||||
"log"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
@ -175,6 +176,9 @@ func handleResize(screen tcell.Screen) bool {
|
|||
|
||||
grid.SetRows(2+(20*blockSize), -1).SetColumns(1, 4+(10*blockSize), 10, -1)
|
||||
|
||||
logMutex.Lock()
|
||||
renderLogMessages = true
|
||||
logMutex.Unlock()
|
||||
draw <- event.DrawAll
|
||||
return true
|
||||
}
|
||||
|
@ -254,15 +258,6 @@ func setInputStatus(active bool) {
|
|||
inputView.SetText("")
|
||||
lowerPages = lowerPages.SwitchToPage("input")
|
||||
} else {
|
||||
msg := inputView.GetText()
|
||||
if msg != "" {
|
||||
if activeGame != nil {
|
||||
activeGame.Event <- &event.MessageEvent{Message: msg}
|
||||
} else {
|
||||
// TODO: Print warning
|
||||
}
|
||||
}
|
||||
|
||||
lowerPages = lowerPages.SwitchToPage("recent")
|
||||
}
|
||||
}
|
||||
|
@ -299,7 +294,20 @@ func renderPreviewMatrix() {
|
|||
side.Clear()
|
||||
side.Write(renderMatrix(g.Players[g.LocalPlayer].Preview))
|
||||
m.Lock()
|
||||
fmt.Fprint(side, fmt.Sprintf("\n\nTime\n\n%.0f\n\nCombo\n\n%d\n\nPending\n\n%d\n\nSpeed\n\n%d", comboTime, combo, m.PendingGarbage, m.Speed))
|
||||
renderLock.Lock()
|
||||
renderBuffer.Reset()
|
||||
if m.Speed < 100 {
|
||||
renderBuffer.WriteRune(' ')
|
||||
}
|
||||
renderBuffer.WriteString(strconv.Itoa(m.Speed))
|
||||
|
||||
if blockSize > 1 {
|
||||
fmt.Fprint(side, fmt.Sprintf("\n\n\n\n\n Combo\n\n %d\n\n\n\n\n Timer\n\n %.0f\n\n\n\n\nPending\n\n %d\n\n\n\n\n Speed\n\n %s", combo, comboTime, m.PendingGarbage, renderBuffer.Bytes()))
|
||||
} else {
|
||||
fmt.Fprint(side, fmt.Sprintf("\n\n Combo\n\n %d\n\n Timer\n\n %.0f\n\nPending\n\n %d\n\n Speed\n\n %s", combo, comboTime, m.PendingGarbage, renderBuffer.Bytes()))
|
||||
}
|
||||
|
||||
renderLock.Unlock()
|
||||
m.Unlock()
|
||||
}
|
||||
|
||||
|
@ -371,17 +379,32 @@ func renderMatrix(m *mino.Matrix) []byte {
|
|||
|
||||
m.DrawPiecesL()
|
||||
|
||||
if m.Preview && blockSize > 2 {
|
||||
blockSize = 2
|
||||
// Draw preview matrix at block size 2 max
|
||||
bs := blockSize
|
||||
if m.Preview {
|
||||
if bs > 2 {
|
||||
bs = 2
|
||||
}
|
||||
if bs > 1 {
|
||||
renderBuffer.WriteRune('\n')
|
||||
}
|
||||
}
|
||||
|
||||
for y := m.H - 1; y >= 0; y-- {
|
||||
for j := 0; j < blockSize; j++ {
|
||||
for j := 0; j < bs; j++ {
|
||||
if !m.Preview {
|
||||
renderBuffer.Write(renderVLine)
|
||||
} else {
|
||||
iPieceNext := m.Bag != nil && m.Bag.Next().String() == mino.TetrominoI
|
||||
if bs == 1 {
|
||||
renderBuffer.WriteRune(' ')
|
||||
renderBuffer.WriteRune(' ')
|
||||
} else if !iPieceNext {
|
||||
renderBuffer.WriteRune(' ')
|
||||
}
|
||||
}
|
||||
for x := 0; x < m.W; x++ {
|
||||
for k := 0; k < blockSize; k++ {
|
||||
for k := 0; k < bs; k++ {
|
||||
renderBuffer.Write(renderBlock[m.Block(x, y)])
|
||||
}
|
||||
}
|
||||
|
@ -399,17 +422,17 @@ func renderMatrix(m *mino.Matrix) []byte {
|
|||
}
|
||||
|
||||
renderBuffer.Write(renderLLCorner)
|
||||
for x := 0; x < m.W*blockSize; x++ {
|
||||
for x := 0; x < m.W*bs; x++ {
|
||||
renderBuffer.Write(renderHLine)
|
||||
}
|
||||
renderBuffer.Write(renderLRCorner)
|
||||
|
||||
renderBuffer.WriteRune('\n')
|
||||
name := m.PlayerName
|
||||
if len(name) > m.W*blockSize {
|
||||
name = name[:m.W*blockSize]
|
||||
if len(name) > m.W*bs {
|
||||
name = name[:m.W*bs]
|
||||
}
|
||||
padName := ((m.W*blockSize - len(name)) / 2) + 1
|
||||
padName := ((m.W*bs - len(name)) / 2) + 1
|
||||
for i := 0; i < padName; i++ {
|
||||
renderBuffer.WriteRune(' ')
|
||||
}
|
||||
|
@ -506,6 +529,13 @@ func renderMatrixes(mx []*mino.Matrix) []byte {
|
|||
return renderBuffer.Bytes()
|
||||
}
|
||||
|
||||
func logMessage(message string) {
|
||||
logMutex.Lock()
|
||||
logMessages = append(logMessages, message)
|
||||
renderLogMessages = true
|
||||
logMutex.Unlock()
|
||||
}
|
||||
|
||||
func renderRecentMessages() {
|
||||
logMutex.Lock()
|
||||
if !renderLogMessages {
|
||||
|
|
|
@ -1,85 +1,95 @@
|
|||
package main
|
||||
|
||||
import "github.com/gdamore/tcell"
|
||||
import (
|
||||
"git.sr.ht/~tslocum/netris/pkg/event"
|
||||
"github.com/gdamore/tcell"
|
||||
)
|
||||
|
||||
func handleKeypress(event *tcell.EventKey) *tcell.EventKey {
|
||||
func handleKeypress(ev *tcell.EventKey) *tcell.EventKey {
|
||||
if inputActive {
|
||||
if event.Key() == tcell.KeyEnter {
|
||||
// TODO: Process
|
||||
if ev.Key() == tcell.KeyEnter {
|
||||
msg := inputView.GetText()
|
||||
if msg != "" {
|
||||
if activeGame != nil {
|
||||
activeGame.Event <- &event.MessageEvent{Message: msg}
|
||||
} else {
|
||||
logMessage("Message not sent - not currently connected to any game")
|
||||
}
|
||||
}
|
||||
|
||||
setInputStatus(false)
|
||||
} else if event.Key() == tcell.KeyEscape {
|
||||
} else if ev.Key() == tcell.KeyEscape {
|
||||
setInputStatus(false)
|
||||
}
|
||||
|
||||
return event
|
||||
return ev
|
||||
}
|
||||
|
||||
if event.Key() == tcell.KeyUp {
|
||||
if ev.Key() == tcell.KeyUp {
|
||||
if activeGame == nil {
|
||||
return event
|
||||
return ev
|
||||
}
|
||||
|
||||
activeGame.Lock()
|
||||
activeGame.Players[activeGame.LocalPlayer].Matrix.HardDropPiece(0)
|
||||
activeGame.Players[activeGame.LocalPlayer].Matrix.HardDropPiece()
|
||||
activeGame.Unlock()
|
||||
|
||||
return nil
|
||||
} else if event.Key() == tcell.KeyDown {
|
||||
} else if ev.Key() == tcell.KeyDown {
|
||||
if activeGame == nil {
|
||||
return event
|
||||
return ev
|
||||
}
|
||||
|
||||
activeGame.Lock()
|
||||
activeGame.Players[activeGame.LocalPlayer].Matrix.MovePiece(0, 0, -1)
|
||||
activeGame.Players[activeGame.LocalPlayer].Matrix.MovePiece(0, -1)
|
||||
activeGame.Unlock()
|
||||
|
||||
return nil
|
||||
} else if event.Key() == tcell.KeyLeft {
|
||||
} else if ev.Key() == tcell.KeyLeft {
|
||||
if activeGame == nil {
|
||||
return event
|
||||
return ev
|
||||
}
|
||||
|
||||
activeGame.Lock()
|
||||
activeGame.Players[activeGame.LocalPlayer].Matrix.MovePiece(0, -1, 0)
|
||||
activeGame.Players[activeGame.LocalPlayer].Matrix.MovePiece(-1, 0)
|
||||
activeGame.Unlock()
|
||||
|
||||
return nil
|
||||
} else if event.Key() == tcell.KeyRight {
|
||||
} else if ev.Key() == tcell.KeyRight {
|
||||
if activeGame == nil {
|
||||
return event
|
||||
return ev
|
||||
}
|
||||
|
||||
activeGame.Lock()
|
||||
activeGame.Players[activeGame.LocalPlayer].Matrix.MovePiece(0, 1, 0)
|
||||
activeGame.Players[activeGame.LocalPlayer].Matrix.MovePiece(1, 0)
|
||||
activeGame.Unlock()
|
||||
|
||||
return nil
|
||||
} else if event.Rune() == 'z' || event.Rune() == 'Z' {
|
||||
} else if ev.Rune() == 'z' || ev.Rune() == 'Z' {
|
||||
if activeGame == nil {
|
||||
return event
|
||||
return ev
|
||||
}
|
||||
|
||||
activeGame.Lock()
|
||||
activeGame.Players[activeGame.LocalPlayer].Matrix.RotatePiece(0, 1, 1)
|
||||
activeGame.Players[activeGame.LocalPlayer].Matrix.RotatePiece(1, 1)
|
||||
activeGame.Unlock()
|
||||
|
||||
return nil
|
||||
} else if event.Rune() == 'x' || event.Rune() == 'X' {
|
||||
} else if ev.Rune() == 'x' || ev.Rune() == 'X' {
|
||||
if activeGame == nil {
|
||||
return event
|
||||
return ev
|
||||
}
|
||||
|
||||
activeGame.Lock()
|
||||
activeGame.Players[activeGame.LocalPlayer].Matrix.RotatePiece(0, 1, 0)
|
||||
activeGame.Players[activeGame.LocalPlayer].Matrix.RotatePiece(1, 0)
|
||||
activeGame.Unlock()
|
||||
|
||||
return nil
|
||||
} else if event.Key() == tcell.KeyEnter {
|
||||
} else if ev.Key() == tcell.KeyEnter {
|
||||
setInputStatus(!inputActive)
|
||||
} else if event.Key() == tcell.KeyEscape {
|
||||
} else if ev.Key() == tcell.KeyEscape {
|
||||
done <- true
|
||||
}
|
||||
|
||||
return event
|
||||
return ev
|
||||
}
|
||||
|
|
|
@ -61,5 +61,3 @@ func BenchmarkRenderLargeMatrix(b *testing.B) {
|
|||
|
||||
blockSize = 1
|
||||
}
|
||||
|
||||
// TODO Check cpu profile of testrendermatrix
|
||||
|
|
|
@ -53,16 +53,15 @@ func main() {
|
|||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
closeGUI()
|
||||
|
||||
time.Sleep(time.Second)
|
||||
|
||||
log.Println()
|
||||
log.Println()
|
||||
log.Println()
|
||||
log.Println()
|
||||
debug.PrintStack()
|
||||
|
||||
log.Println()
|
||||
log.Println()
|
||||
log.Fatalf("panic: %+v", r)
|
||||
os.Exit(0)
|
||||
}
|
||||
}()
|
||||
|
||||
|
@ -80,7 +79,6 @@ func main() {
|
|||
log.Fatal("failed to start netris: non-interactive terminals are not supported")
|
||||
}
|
||||
|
||||
// TODO Document
|
||||
if blockSize > 3 {
|
||||
blockSize = 3
|
||||
}
|
||||
|
@ -117,10 +115,7 @@ func main() {
|
|||
logger := make(chan string, game.LogQueueSize)
|
||||
go func() {
|
||||
for msg := range logger {
|
||||
logMutex.Lock()
|
||||
logMessages = append(logMessages, time.Now().Format(LogTimeFormat)+" "+msg)
|
||||
renderLogMessages = true
|
||||
logMutex.Unlock()
|
||||
logMessage(time.Now().Format(LogTimeFormat) + " " + msg)
|
||||
}
|
||||
}()
|
||||
|
||||
|
@ -158,10 +153,7 @@ func main() {
|
|||
if logDebug || logVerbose {
|
||||
go func() {
|
||||
for msg := range server.Logger {
|
||||
logMutex.Lock()
|
||||
logMessages = append(logMessages, time.Now().Format(LogTimeFormat)+" Local server: "+msg)
|
||||
renderLogMessages = true
|
||||
logMutex.Unlock()
|
||||
logMessage(time.Now().Format(LogTimeFormat) + " Local server: " + msg)
|
||||
}
|
||||
}()
|
||||
} else {
|
||||
|
|
|
@ -12,7 +12,7 @@ import (
|
|||
"git.sr.ht/~tslocum/netris/pkg/mino"
|
||||
)
|
||||
|
||||
const UpdateDuration = 750 * time.Millisecond
|
||||
const UpdateDuration = 850 * time.Millisecond
|
||||
|
||||
const (
|
||||
LogStandard = iota
|
||||
|
@ -365,10 +365,6 @@ func (g *Game) handleDistributeMatrixes() {
|
|||
return
|
||||
}
|
||||
|
||||
// TODO: Check for inactive players and disconnect them
|
||||
// Assign unique player ID and use maps instead of slices?
|
||||
// Assign matrix, etc to player instead, and use only one slice of players?
|
||||
|
||||
remainingPlayer := -1
|
||||
remainingPlayers := 0
|
||||
|
||||
|
@ -444,8 +440,10 @@ func (g *Game) HandleReadCommands(in chan GameCommandInterface) {
|
|||
for e = range in {
|
||||
g.Lock()
|
||||
|
||||
c := e.Command()
|
||||
|
||||
logLevel := LogDebug
|
||||
if e.Command() == CommandUpdateMatrix {
|
||||
if c == CommandPing || c == CommandPong || c == CommandUpdateMatrix {
|
||||
logLevel = LogVerbose
|
||||
}
|
||||
g.Log(logLevel, "LOCAL handle ", e.Command(), " from ", e.Source(), " ", e)
|
||||
|
@ -471,23 +469,7 @@ func (g *Game) HandleReadCommands(in chan GameCommandInterface) {
|
|||
}
|
||||
case CommandUpdateGame:
|
||||
if p, ok := e.(*GameCommandUpdateGame); ok {
|
||||
for playerID, playerName := range p.Players {
|
||||
if existingPlayer, ok := g.Players[playerID]; ok {
|
||||
existingPlayer.Name = playerName
|
||||
} else {
|
||||
pl := NewPlayer(playerName, nil)
|
||||
pl.Player = playerID
|
||||
|
||||
g.AddPlayerL(pl)
|
||||
}
|
||||
}
|
||||
for playerID := range g.Players {
|
||||
if _, ok := p.Players[playerID]; !ok {
|
||||
g.RemovePlayerL(playerID)
|
||||
}
|
||||
}
|
||||
|
||||
g.draw <- event.DrawMultiplayerMatrixes
|
||||
g.processUpdateGameL(p)
|
||||
}
|
||||
case CommandStartGame:
|
||||
if p, ok := e.(*GameCommandStartGame); ok {
|
||||
|
@ -619,3 +601,30 @@ func (g *Game) handleLowerPiece() {
|
|||
g.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
func (g *Game) processUpdateGame(gc *GameCommandUpdateGame) {
|
||||
g.Lock()
|
||||
defer g.Unlock()
|
||||
|
||||
g.processUpdateGameL(gc)
|
||||
}
|
||||
|
||||
func (g *Game) processUpdateGameL(gc *GameCommandUpdateGame) {
|
||||
for playerID, playerName := range gc.Players {
|
||||
if existingPlayer, ok := g.Players[playerID]; ok {
|
||||
existingPlayer.Name = playerName
|
||||
} else {
|
||||
pl := NewPlayer(playerName, nil)
|
||||
pl.Player = playerID
|
||||
|
||||
g.AddPlayerL(pl)
|
||||
}
|
||||
}
|
||||
for playerID := range g.Players {
|
||||
if _, ok := gc.Players[playerID]; !ok {
|
||||
g.RemovePlayerL(playerID)
|
||||
}
|
||||
}
|
||||
|
||||
g.draw <- event.DrawMultiplayerMatrixes
|
||||
}
|
||||
|
|
|
@ -143,6 +143,8 @@ func (c Command) String() string {
|
|||
const (
|
||||
CommandUnknown Command = iota
|
||||
CommandDisconnect
|
||||
CommandPing
|
||||
CommandPong
|
||||
CommandNickname
|
||||
CommandMessage
|
||||
CommandNewGame
|
||||
|
@ -182,6 +184,24 @@ type GameCommandInterface interface {
|
|||
SetSource(int)
|
||||
}
|
||||
|
||||
type GameCommandPing struct {
|
||||
GameCommand
|
||||
Message string
|
||||
}
|
||||
|
||||
func (gc GameCommandPing) Command() Command {
|
||||
return CommandPing
|
||||
}
|
||||
|
||||
type GameCommandPong struct {
|
||||
GameCommand
|
||||
Message string
|
||||
}
|
||||
|
||||
func (gc GameCommandPong) Command() Command {
|
||||
return CommandPong
|
||||
}
|
||||
|
||||
type GameCommandMessage struct {
|
||||
GameCommand
|
||||
Player int
|
||||
|
|
|
@ -83,9 +83,6 @@ func (s *Server) NewGame() (*Game, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
// TODO
|
||||
//g.LogLevel = LogDebug
|
||||
|
||||
s.Games[gameID] = g
|
||||
|
||||
return g, nil
|
||||
|
@ -206,6 +203,8 @@ func (s *Server) handleJoinGame(pl *Player) {
|
|||
|
||||
s.Log("JOINING GAME", p)
|
||||
|
||||
pl.Write(&GameCommandMessage{Message: "Welcome to netris"})
|
||||
|
||||
g := s.FindGame(pl, p.GameID)
|
||||
|
||||
s.Log("New player added to game", *pl, p.GameID)
|
||||
|
@ -237,13 +236,14 @@ func (s *Server) initiateAutoStart(g *Game) {
|
|||
func (s *Server) handleGameCommands(pl *Player, g *Game) {
|
||||
s.Log("waiting first msg handle game commands")
|
||||
for e := range pl.In {
|
||||
if e.Command() != CommandUpdateMatrix || g.LogLevel >= LogVerbose {
|
||||
c := e.Command()
|
||||
if (c != CommandPing && c != CommandPong && c != CommandUpdateMatrix) || g.LogLevel >= LogVerbose {
|
||||
s.Log("REMOTE handle game command ", e.Command(), " from ", e.Source(), e)
|
||||
}
|
||||
|
||||
g.Lock()
|
||||
|
||||
switch e.Command() {
|
||||
switch c {
|
||||
case CommandMessage:
|
||||
if p, ok := e.(*GameCommandMessage); ok {
|
||||
if player, ok := g.Players[p.SourcePlayer]; ok {
|
||||
|
|
|
@ -27,6 +27,8 @@ type ServerConn struct {
|
|||
out chan GameCommandInterface
|
||||
ForwardOut chan GameCommandInterface
|
||||
|
||||
LastTransfer time.Time
|
||||
|
||||
Terminated bool
|
||||
|
||||
*sync.WaitGroup
|
||||
|
@ -39,6 +41,8 @@ func NewServerConn(conn net.Conn, forwardOut chan GameCommandInterface) *ServerC
|
|||
s.out = make(chan GameCommandInterface, CommandQueueSize)
|
||||
s.ForwardOut = forwardOut
|
||||
|
||||
s.LastTransfer = time.Now()
|
||||
|
||||
if conn == nil {
|
||||
// Local instance
|
||||
|
||||
|
@ -46,6 +50,7 @@ func NewServerConn(conn net.Conn, forwardOut chan GameCommandInterface) *ServerC
|
|||
} else {
|
||||
go s.handleRead()
|
||||
go s.handleWrite()
|
||||
go s.handleSendKeepAlive()
|
||||
}
|
||||
|
||||
return &s
|
||||
|
@ -81,6 +86,21 @@ func Connect(address string) *ServerConn {
|
|||
}
|
||||
}
|
||||
|
||||
func (s *ServerConn) handleSendKeepAlive() {
|
||||
t := time.NewTicker(7 * time.Second)
|
||||
for {
|
||||
<-t.C
|
||||
|
||||
if s.Terminated {
|
||||
t.Stop()
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: Only send when necessary
|
||||
s.Write(&GameCommandPing{})
|
||||
}
|
||||
}
|
||||
|
||||
func (s *ServerConn) Write(gc GameCommandInterface) {
|
||||
if s == nil || s.Terminated {
|
||||
return
|
||||
|
@ -116,17 +136,40 @@ func (s *ServerConn) handleRead() {
|
|||
}
|
||||
|
||||
var (
|
||||
msg GameCommandTransport
|
||||
gc GameCommandInterface
|
||||
msg GameCommandTransport
|
||||
gc GameCommandInterface
|
||||
processed bool
|
||||
)
|
||||
scanner := bufio.NewScanner(s.Conn)
|
||||
for scanner.Scan() {
|
||||
processed = false
|
||||
|
||||
err := json.Unmarshal(scanner.Bytes(), &msg)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if msg.Command == CommandMessage {
|
||||
s.LastTransfer = time.Now()
|
||||
|
||||
if msg.Command == CommandPing {
|
||||
var gameCommand GameCommandPing
|
||||
err := json.Unmarshal(msg.Data, &gameCommand)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
s.Write(&GameCommandPong{Message: gameCommand.Message})
|
||||
processed = true
|
||||
} else if msg.Command == CommandPong {
|
||||
var gameCommand GameCommandPong
|
||||
err := json.Unmarshal(msg.Data, &gameCommand)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// TODO: Verify
|
||||
processed = true
|
||||
} else if msg.Command == CommandMessage {
|
||||
var gameCommand GameCommandMessage
|
||||
err := json.Unmarshal(msg.Data, &gameCommand)
|
||||
if err != nil {
|
||||
|
@ -203,8 +246,10 @@ func (s *ServerConn) handleRead() {
|
|||
continue
|
||||
}
|
||||
|
||||
s.addSourceID(gc)
|
||||
s.In <- gc
|
||||
if !processed {
|
||||
s.addSourceID(gc)
|
||||
s.In <- gc
|
||||
}
|
||||
|
||||
err = s.Conn.SetReadDeadline(time.Now().Add(ConnTimeout))
|
||||
if err != nil {
|
||||
|
@ -212,6 +257,8 @@ func (s *ServerConn) handleRead() {
|
|||
return
|
||||
}
|
||||
}
|
||||
|
||||
s.Close()
|
||||
}
|
||||
|
||||
func (s *ServerConn) handleWrite() {
|
||||
|
@ -234,7 +281,17 @@ func (s *ServerConn) handleWrite() {
|
|||
}
|
||||
|
||||
msg = GameCommandTransport{Command: e.Command()}
|
||||
if p, ok := e.(*GameCommandMessage); ok {
|
||||
if p, ok := e.(*GameCommandPing); ok {
|
||||
msg.Data, err = json.Marshal(p)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
} else if p, ok := e.(*GameCommandPong); ok {
|
||||
msg.Data, err = json.Marshal(p)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
} else if p, ok := e.(*GameCommandMessage); ok {
|
||||
msg.Data, err = json.Marshal(p)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
@ -300,6 +357,8 @@ func (s *ServerConn) handleWrite() {
|
|||
s.Close()
|
||||
}
|
||||
|
||||
s.LastTransfer = time.Now()
|
||||
|
||||
err = s.Conn.SetWriteDeadline(time.Time{})
|
||||
|
||||
s.Done()
|
||||
|
@ -334,18 +393,19 @@ func (s *ServerConn) JoinGame(name string, gameID int, logger chan string, draw
|
|||
switch e.Command() {
|
||||
case CommandMessage:
|
||||
if p, ok := e.(*GameCommandMessage); ok {
|
||||
if g != nil {
|
||||
prefix := "* "
|
||||
if p.Player > 0 {
|
||||
name := "Anonymous"
|
||||
if player, ok := g.Players[p.Player]; ok {
|
||||
name = player.Name
|
||||
}
|
||||
prefix = "<" + name + "> "
|
||||
prefix := "* "
|
||||
if p.Player > 0 {
|
||||
name := "Anonymous"
|
||||
if player, ok := g.Players[p.Player]; ok {
|
||||
name = player.Name
|
||||
}
|
||||
prefix = "<" + name + "> "
|
||||
}
|
||||
|
||||
if g != nil {
|
||||
g.Log(LogStandard, prefix+p.Message)
|
||||
} else {
|
||||
logger <- p.Message
|
||||
logger <- prefix + p.Message
|
||||
draw <- event.DrawMessages
|
||||
}
|
||||
}
|
||||
|
@ -366,22 +426,7 @@ func (s *ServerConn) JoinGame(name string, gameID int, logger chan string, draw
|
|||
}
|
||||
|
||||
if p, ok := e.(*GameCommandUpdateGame); ok {
|
||||
// TODO Unify with JoinGame player update
|
||||
g.Lock()
|
||||
for playerID, playerName := range p.Players {
|
||||
if existingPlayer, ok := g.Players[playerID]; ok {
|
||||
existingPlayer.Name = playerName
|
||||
} else {
|
||||
pl := NewPlayer(playerName, nil)
|
||||
pl.Player = playerID
|
||||
|
||||
g.AddPlayerL(pl)
|
||||
}
|
||||
}
|
||||
g.Unlock()
|
||||
} else {
|
||||
log.Println(e.Command(), " - ", e)
|
||||
panic("unknown payload")
|
||||
g.processUpdateGame(p)
|
||||
}
|
||||
case CommandStartGame:
|
||||
if p, ok := e.(*GameCommandStartGame); ok {
|
||||
|
@ -397,10 +442,6 @@ func (s *ServerConn) JoinGame(name string, gameID int, logger chan string, draw
|
|||
return g, nil
|
||||
}
|
||||
}
|
||||
case CommandUpdateMatrix:
|
||||
// Do nothing (missed join game command)
|
||||
default:
|
||||
log.Println("unnknown joingame command", e.Command(), e)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -11,16 +11,18 @@ import (
|
|||
"git.sr.ht/~tslocum/netris/pkg/event"
|
||||
)
|
||||
|
||||
const GarbageDelayTicks = 3
|
||||
const GarbageDelay = 1500 * time.Millisecond
|
||||
|
||||
type Matrix struct {
|
||||
W, H, B int // TODO: Implement buffer zone
|
||||
W int `json:"-"` // Width
|
||||
H int `json:"-"` // Height
|
||||
B int `json:"-"` // Buffer height
|
||||
|
||||
M map[int]Block // Matrix
|
||||
O map[int]Block // Overlay
|
||||
|
||||
Bag *Bag `json:"-"`
|
||||
P []*Piece // Pieces
|
||||
Bag *Bag `json:"-"`
|
||||
P *Piece
|
||||
PlayerName string
|
||||
|
||||
Preview bool
|
||||
|
@ -29,12 +31,12 @@ type Matrix struct {
|
|||
Move chan int `json:"-"`
|
||||
draw chan event.DrawObject `json:"-"`
|
||||
|
||||
Combo int
|
||||
ComboStart time.Time
|
||||
ComboEnd time.Time
|
||||
PendingGarbage int
|
||||
PendingGarbageTicks int
|
||||
Speed int
|
||||
Combo int
|
||||
ComboStart time.Time `json:"-"`
|
||||
ComboEnd time.Time `json:"-"`
|
||||
PendingGarbage int `json:"-"`
|
||||
PendingGarbageTime time.Time `json:"-"`
|
||||
Speed int
|
||||
|
||||
GameOver bool
|
||||
|
||||
|
@ -111,11 +113,7 @@ func (m *Matrix) takePiece() bool {
|
|||
|
||||
p.Point = pieceStart
|
||||
|
||||
if m.P != nil {
|
||||
m.P[0] = p
|
||||
} else {
|
||||
m.P = append(m.P, p)
|
||||
}
|
||||
m.P = p
|
||||
|
||||
return true
|
||||
}
|
||||
|
@ -285,7 +283,7 @@ func (m *Matrix) AddPendingGarbage(lines int) {
|
|||
defer m.Unlock()
|
||||
|
||||
if m.PendingGarbage == 0 {
|
||||
m.PendingGarbageTicks = GarbageDelayTicks
|
||||
m.PendingGarbageTime = time.Now().Add(GarbageDelay)
|
||||
}
|
||||
|
||||
m.PendingGarbage += lines
|
||||
|
@ -297,8 +295,7 @@ func (m *Matrix) ReceiveGarbage() {
|
|||
|
||||
if m.PendingGarbage == 0 || m.GameOver {
|
||||
return
|
||||
} else if m.PendingGarbageTicks > 0 {
|
||||
m.PendingGarbageTicks--
|
||||
} else if time.Since(m.PendingGarbageTime) < 0 {
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -309,10 +306,6 @@ func (m *Matrix) ReceiveGarbage() {
|
|||
}
|
||||
|
||||
func (m *Matrix) addGarbage(lines int) bool {
|
||||
if len(m.P) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
for my := m.H + m.B; my >= 0; my-- {
|
||||
for mx := 0; mx < m.W; mx++ {
|
||||
if my >= m.H+m.B-lines && m.M[I(mx, my, m.W)] != BlockNone {
|
||||
|
@ -334,18 +327,18 @@ func (m *Matrix) addGarbage(lines int) bool {
|
|||
}
|
||||
}
|
||||
|
||||
y := m.P[0].Y
|
||||
y := m.P.Y
|
||||
for {
|
||||
if y == m.H+m.B {
|
||||
return false
|
||||
} else if m.canAddAt(m.P[0], Point{m.P[0].X, y}) {
|
||||
} else if m.canAddAt(m.P, Point{m.P.X, y}) {
|
||||
break
|
||||
}
|
||||
|
||||
y++
|
||||
}
|
||||
|
||||
m.P[0].Y = y
|
||||
m.P.Y = y
|
||||
|
||||
m.Draw()
|
||||
|
||||
|
@ -385,7 +378,7 @@ func (m *Matrix) Reset() {
|
|||
m.lands = nil
|
||||
m.Speed = 0
|
||||
m.PendingGarbage = 0
|
||||
m.PendingGarbageTicks = 0
|
||||
m.PendingGarbageTime = time.Time{}
|
||||
m.Unlock()
|
||||
|
||||
m.Clear()
|
||||
|
@ -415,11 +408,11 @@ func (m *Matrix) DrawPieces() {
|
|||
func (m *Matrix) DrawPiecesL() {
|
||||
m.clearOverlay()
|
||||
|
||||
if m.GameOver || len(m.P) < 1 {
|
||||
if m.GameOver {
|
||||
return
|
||||
}
|
||||
|
||||
p := m.P[0]
|
||||
p := m.P
|
||||
if p == nil {
|
||||
return
|
||||
}
|
||||
|
@ -527,15 +520,15 @@ func (m *Matrix) SetBlock(x int, y int, block Block, overlay bool) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
func (m *Matrix) RotatePiece(player int, rotations int, direction int) bool {
|
||||
func (m *Matrix) RotatePiece(rotations int, direction int) bool {
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
|
||||
if m.GameOver || rotations == 0 || len(m.P) == 0 {
|
||||
if m.GameOver || rotations == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
p := m.P[player]
|
||||
p := m.P
|
||||
|
||||
originalMino := make(Mino, len(p.Mino))
|
||||
copy(originalMino, p.Mino)
|
||||
|
@ -607,32 +600,32 @@ func (m *Matrix) LowerPiece() {
|
|||
m.Lock()
|
||||
defer m.Unlock()
|
||||
|
||||
if m.GameOver || len(m.P) == 0 {
|
||||
if m.GameOver {
|
||||
return
|
||||
} else if m.canAddAt(m.P[0], Point{m.P[0].X, m.P[0].Y - 1}) {
|
||||
m.movePiece(0, 0, -1)
|
||||
} else if m.canAddAt(m.P, Point{m.P.X, m.P.Y - 1}) {
|
||||
m.movePiece(0, -1)
|
||||
} else {
|
||||
m.landPiece()
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Matrix) finishLandingPiece() {
|
||||
if m.GameOver || len(m.P) == 0 || m.P[0].Landed {
|
||||
if m.GameOver || m.P.Landed {
|
||||
return
|
||||
}
|
||||
|
||||
m.P[0].Landed = true
|
||||
m.P.Landed = true
|
||||
|
||||
dropped := false
|
||||
LANDPIECE:
|
||||
for y := m.P[0].Y; y >= 0; y-- {
|
||||
if y == 0 || !m.canAddAt(m.P[0], Point{m.P[0].X, y - 1}) {
|
||||
for y := m.P.Y; y >= 0; y-- {
|
||||
if y == 0 || !m.canAddAt(m.P, Point{m.P.X, y - 1}) {
|
||||
for dropY := y - 1; dropY < m.H+m.B; dropY++ {
|
||||
if !m.canAddAt(m.P[0], Point{m.P[0].X, dropY}) {
|
||||
if !m.canAddAt(m.P, Point{m.P.X, dropY}) {
|
||||
continue
|
||||
}
|
||||
|
||||
err := m.add(m.P[0], m.P[0].Solid, Point{m.P[0].X, dropY}, false)
|
||||
err := m.add(m.P, m.P.Solid, Point{m.P.X, dropY}, false)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to add piece when landing piece: %+v", err)
|
||||
}
|
||||
|
@ -695,12 +688,12 @@ LANDPIECE:
|
|||
remainingGarbage := sendGarbage
|
||||
if m.PendingGarbage > 0 {
|
||||
m.PendingGarbage -= sendGarbage
|
||||
remainingGarbage = 0
|
||||
|
||||
if m.PendingGarbage <= 0 {
|
||||
if m.PendingGarbage < 0 {
|
||||
remainingGarbage = m.PendingGarbage * -1
|
||||
m.PendingGarbage = 0
|
||||
m.PendingGarbageTicks = 0
|
||||
} else {
|
||||
remainingGarbage = 0
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -772,11 +765,7 @@ func (m *Matrix) CalculateBonusGarbage() int {
|
|||
}
|
||||
|
||||
func (m *Matrix) landPiece() {
|
||||
if len(m.P) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
p := m.P[0]
|
||||
p := m.P
|
||||
p.Lock()
|
||||
if p.Landing || p.Landed || m.GameOver {
|
||||
p.Unlock()
|
||||
|
@ -824,29 +813,29 @@ func (m *Matrix) landPiece() {
|
|||
}()
|
||||
}
|
||||
|
||||
func (m *Matrix) MovePiece(player int, x int, y int) bool {
|
||||
func (m *Matrix) MovePiece(x int, y int) bool {
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
|
||||
return m.movePiece(player, x, y)
|
||||
return m.movePiece(x, y)
|
||||
}
|
||||
|
||||
func (m *Matrix) movePiece(player int, x int, y int) bool {
|
||||
if m.GameOver || len(m.P) == 0 || (x == 0 && y == 0) {
|
||||
func (m *Matrix) movePiece(x int, y int) bool {
|
||||
if m.GameOver || (x == 0 && y == 0) {
|
||||
return false
|
||||
}
|
||||
|
||||
px := m.P[player].X + x
|
||||
py := m.P[player].Y + y
|
||||
px := m.P.X + x
|
||||
py := m.P.Y + y
|
||||
|
||||
if !m.canAddAt(m.P[player], Point{px, py}) {
|
||||
if !m.canAddAt(m.P, Point{px, py}) {
|
||||
return false
|
||||
}
|
||||
|
||||
m.P[0].ApplyReset()
|
||||
m.P[0].SetLocation(px, py)
|
||||
m.P.ApplyReset()
|
||||
m.P.SetLocation(px, py)
|
||||
|
||||
if !m.canAddAt(m.P[0], Point{m.P[0].X, m.P[0].Y - 1}) {
|
||||
if !m.canAddAt(m.P, Point{m.P.X, m.P.Y - 1}) {
|
||||
m.landPiece()
|
||||
}
|
||||
|
||||
|
@ -867,7 +856,7 @@ func (m *Matrix) Moved() {
|
|||
m.Move <- 0
|
||||
}
|
||||
|
||||
func (m *Matrix) HardDropPiece(player int) {
|
||||
func (m *Matrix) HardDropPiece() {
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
|
||||
|
@ -880,28 +869,9 @@ func (m *Matrix) Replace(newmtx *Matrix) {
|
|||
|
||||
m.W, m.H, m.B = newmtx.W, newmtx.H, newmtx.B
|
||||
m.M = newmtx.M
|
||||
/*for i := range newmtx.P {
|
||||
// TODO
|
||||
if newmtx.P[i].Mutex == nil {
|
||||
if i < len(m.P) {
|
||||
m.P[i].Lock()
|
||||
|
||||
newmtx.P[i].Mutex = m.P[i].Mutex
|
||||
} else {
|
||||
newmtx.P[i].Mutex = new(sync.Mutex)
|
||||
|
||||
newmtx.P[i].Lock()
|
||||
}
|
||||
}
|
||||
}*/
|
||||
m.P = newmtx.P
|
||||
/*for i := range m.P {
|
||||
m.P[i].Unlock()
|
||||
}*/
|
||||
m.Preview = newmtx.Preview
|
||||
m.Speed = newmtx.Speed
|
||||
|
||||
// TODO: Show opponents pending garbage
|
||||
}
|
||||
|
||||
func fibonacci(value int) int {
|
||||
|
|
|
@ -10,29 +10,68 @@ func TestMatrix(t *testing.T) {
|
|||
t.Error(err)
|
||||
}
|
||||
|
||||
err = m.Add(m.P[0], BlockSolidBlue, Point{3, 3}, false)
|
||||
m.AddTestBlocks()
|
||||
|
||||
ok := m.SetBlock(9, 0, BlockSolidMagenta, false)
|
||||
if !ok {
|
||||
t.Error("failed to set final block after test blocks")
|
||||
}
|
||||
ok = m.SetBlock(9, 1, BlockSolidMagenta, false)
|
||||
if !ok {
|
||||
t.Error("failed to set final block after test blocks")
|
||||
}
|
||||
ok = m.SetBlock(9, 3, BlockSolidMagenta, false)
|
||||
if !ok {
|
||||
t.Error("failed to set final block after test blocks")
|
||||
}
|
||||
|
||||
cleared := m.clearFilled()
|
||||
if cleared != 3 {
|
||||
t.Errorf("failed to clear lines, wanted 3 got %d", cleared)
|
||||
}
|
||||
|
||||
m.Clear()
|
||||
|
||||
err = m.Add(m.P, BlockSolidBlue, Point{3, 3}, false)
|
||||
if err != nil {
|
||||
t.Errorf("failed to add initial mino to matrix: %s", err)
|
||||
}
|
||||
|
||||
err = m.Add(m.P[0], BlockSolidBlue, Point{3, 3}, false)
|
||||
err = m.Add(m.P, BlockSolidBlue, Point{3, 3}, false)
|
||||
if err == nil {
|
||||
t.Error("failed to detect collision when adding second mino to matrix")
|
||||
}
|
||||
|
||||
err = m.Add(m.P[0], BlockSolidBlue, Point{9, 9}, false)
|
||||
err = m.Add(m.P, BlockSolidBlue, Point{9, 9}, false)
|
||||
if err == nil {
|
||||
t.Error("failed to detect out of bounds when adding third mino to matrix")
|
||||
}
|
||||
|
||||
m.Clear()
|
||||
|
||||
for i := 0; i < 8; i++ {
|
||||
ok := m.RotatePiece(0, 1, 0)
|
||||
for i := 0; i < 11; i++ {
|
||||
ok := m.RotatePiece(1, 0)
|
||||
if !ok {
|
||||
t.Errorf("failed to rotate piece on iteration %d", i)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Add rotate and wall kick tests
|
||||
for i := 0; i < 4; i++ {
|
||||
ok := m.movePiece(1, 0)
|
||||
if !ok {
|
||||
t.Errorf("failed to move piece on iteration %d", i)
|
||||
}
|
||||
}
|
||||
|
||||
ok = m.RotatePiece(1, 0)
|
||||
if !ok {
|
||||
t.Errorf("failed to rotate piece for right wall kick")
|
||||
}
|
||||
|
||||
for i := 0; i < 7; i++ {
|
||||
ok := m.movePiece(-1, 0)
|
||||
if !ok {
|
||||
t.Errorf("failed to move piece on iteration %d", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,20 +0,0 @@
|
|||
package mino
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestPiece(t *testing.T) {
|
||||
minos, err := Generate(4)
|
||||
if err != nil {
|
||||
t.Errorf("failed to generate minos for rank %d: %s", 4, err)
|
||||
}
|
||||
|
||||
if len(minos) != 7 {
|
||||
t.Errorf("failed to generate minos for rank %d: unexpected number of minos generated", 4)
|
||||
}
|
||||
|
||||
p := NewPiece(minos[2], Point{0, 0})
|
||||
// TODO
|
||||
_ = p
|
||||
}
|
Loading…
Reference in New Issue