MVP
This commit is contained in:
parent
15e7f20f1a
commit
067d22f3a9
51
.air_client.toml
Normal file
51
.air_client.toml
Normal file
|
@ -0,0 +1,51 @@
|
|||
root = "."
|
||||
testdata_dir = "testdata"
|
||||
tmp_dir = "tmp"
|
||||
|
||||
[build]
|
||||
args_bin = []
|
||||
bin = "./tmp/client/main"
|
||||
cmd = "go build -o ./tmp/client/ ./cmd/client/main.go"
|
||||
delay = 1000
|
||||
exclude_dir = ["assets", "tmp", "vendor", "testdata"]
|
||||
exclude_file = []
|
||||
exclude_regex = ["_test.go"]
|
||||
exclude_unchanged = false
|
||||
follow_symlink = false
|
||||
full_bin = ""
|
||||
include_dir = []
|
||||
include_ext = ["go", "tpl", "tmpl", "html"]
|
||||
include_file = []
|
||||
kill_delay = "0s"
|
||||
log = "build-errors.log"
|
||||
poll = false
|
||||
poll_interval = 0
|
||||
post_cmd = []
|
||||
pre_cmd = []
|
||||
rerun = false
|
||||
rerun_delay = 500
|
||||
send_interrupt = false
|
||||
stop_on_error = false
|
||||
|
||||
[color]
|
||||
app = ""
|
||||
build = "yellow"
|
||||
main = "magenta"
|
||||
runner = "green"
|
||||
watcher = "cyan"
|
||||
|
||||
[log]
|
||||
main_only = false
|
||||
time = false
|
||||
|
||||
[misc]
|
||||
clean_on_exit = false
|
||||
|
||||
[proxy]
|
||||
app_port = 0
|
||||
enabled = false
|
||||
proxy_port = 0
|
||||
|
||||
[screen]
|
||||
clear_on_rebuild = false
|
||||
keep_scroll = true
|
51
.air_server.toml
Normal file
51
.air_server.toml
Normal file
|
@ -0,0 +1,51 @@
|
|||
root = "."
|
||||
testdata_dir = "testdata"
|
||||
tmp_dir = "tmp"
|
||||
|
||||
[build]
|
||||
args_bin = []
|
||||
bin = "./tmp/server/main"
|
||||
cmd = "go build -o ./tmp/server/ ./cmd/server/main.go"
|
||||
delay = 1000
|
||||
exclude_dir = ["assets", "tmp", "vendor", "testdata"]
|
||||
exclude_file = []
|
||||
exclude_regex = ["_test.go"]
|
||||
exclude_unchanged = false
|
||||
follow_symlink = false
|
||||
full_bin = ""
|
||||
include_dir = []
|
||||
include_ext = ["go", "tpl", "tmpl", "html"]
|
||||
include_file = []
|
||||
kill_delay = "0s"
|
||||
log = "build-errors.log"
|
||||
poll = false
|
||||
poll_interval = 0
|
||||
post_cmd = []
|
||||
pre_cmd = []
|
||||
rerun = false
|
||||
rerun_delay = 500
|
||||
send_interrupt = false
|
||||
stop_on_error = false
|
||||
|
||||
[color]
|
||||
app = ""
|
||||
build = "yellow"
|
||||
main = "magenta"
|
||||
runner = "green"
|
||||
watcher = "cyan"
|
||||
|
||||
[log]
|
||||
main_only = false
|
||||
time = false
|
||||
|
||||
[misc]
|
||||
clean_on_exit = false
|
||||
|
||||
[proxy]
|
||||
app_port = 0
|
||||
enabled = false
|
||||
proxy_port = 0
|
||||
|
||||
[screen]
|
||||
clear_on_rebuild = false
|
||||
keep_scroll = true
|
|
@ -8,6 +8,7 @@ import (
|
|||
"net"
|
||||
"os"
|
||||
"sshpong/internal/client"
|
||||
"sshpong/internal/config"
|
||||
"sshpong/internal/lobby"
|
||||
"strings"
|
||||
)
|
||||
|
@ -15,25 +16,43 @@ import (
|
|||
var username string
|
||||
|
||||
func main() {
|
||||
slog.SetLogLoggerLevel(slog.LevelDebug)
|
||||
slog.Debug("Debug logs active...")
|
||||
if len(os.Args) == 1 {
|
||||
config.LoadConfig("")
|
||||
} else {
|
||||
config.LoadConfig(os.Args[1])
|
||||
}
|
||||
|
||||
slog.SetLogLoggerLevel(slog.Level(config.Config.LogLevel))
|
||||
|
||||
fmt.Println("Welcome to sshpong!")
|
||||
fmt.Println("Please enter your username")
|
||||
|
||||
egress := make(chan []byte)
|
||||
ingress := make(chan []byte)
|
||||
interrupter := make(chan client.InterrupterMessage, 100)
|
||||
exit := make(chan string)
|
||||
|
||||
buf := make([]byte, 1024)
|
||||
n, err := os.Stdin.Read(buf)
|
||||
if err != nil {
|
||||
log.Panic("Bro your input is no good...")
|
||||
}
|
||||
username = string(buf[:n-1])
|
||||
var usernameOk = false
|
||||
|
||||
// In the future make a DB call as well?
|
||||
isUsernameOk := func(un string) bool {
|
||||
if strings.Contains(un, ":") || len(strings.Split(un, " ")) > 1 || len(un) < 1 {
|
||||
fmt.Println(client.Red, "Sorry, please pick a username that has no special characters or spaces.", client.Normal)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
for !usernameOk {
|
||||
fmt.Println("Please enter your username")
|
||||
buf := make([]byte, 1024)
|
||||
n, err := os.Stdin.Read(buf)
|
||||
if err != nil {
|
||||
log.Panic("Bro your input is no good...")
|
||||
}
|
||||
username = string(buf[:n-1])
|
||||
usernameOk = isUsernameOk(username)
|
||||
}
|
||||
|
||||
fmt.Println("username is...", username)
|
||||
conn, err := ConnectToLobby(username)
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
|
@ -55,6 +74,11 @@ func main() {
|
|||
|
||||
select {
|
||||
case msg := <-interrupter:
|
||||
if msg.InterruptType == "start_game" {
|
||||
slog.Debug("closing input handler with start_game message and sending exit signal")
|
||||
exit <- msg.Content
|
||||
return
|
||||
}
|
||||
userMessage, err := client.HandleInterruptInput(msg, args, username)
|
||||
if err != nil {
|
||||
userMessage, err = client.HandleUserInput(args, username)
|
||||
|
@ -67,16 +91,6 @@ func main() {
|
|||
}
|
||||
}
|
||||
egress <- userMessage
|
||||
if userMessage[0] == lobby.Accept || userMessage[0] == lobby.Disconnect {
|
||||
slog.Debug("Closing input handler with accept or disconnect message")
|
||||
return
|
||||
}
|
||||
if userMessage[0] == lobby.StartGame {
|
||||
slog.Debug("closing input handler with start_game message and sending exit signal")
|
||||
|
||||
exit <- msg.Content
|
||||
return
|
||||
}
|
||||
|
||||
default:
|
||||
userMessage, err = client.HandleUserInput(args, username)
|
||||
|
@ -88,9 +102,7 @@ func main() {
|
|||
continue
|
||||
}
|
||||
egress <- userMessage
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}()
|
||||
|
||||
|
@ -103,6 +115,9 @@ func main() {
|
|||
if err != nil {
|
||||
log.Panic("Error handling server message disconnecting...")
|
||||
}
|
||||
if interrupterMsg.InterruptType == "start_game" {
|
||||
exit <- interrupterMsg.Content
|
||||
}
|
||||
if interrupterMsg.InterruptType != "" {
|
||||
interrupter <- interrupterMsg
|
||||
}
|
||||
|
@ -120,10 +135,6 @@ func main() {
|
|||
if err == io.EOF {
|
||||
log.Panic("Server disconnected, sorry...")
|
||||
}
|
||||
if msg[0] == lobby.StartGame || msg[0] == lobby.Disconnect {
|
||||
slog.Debug("closing network writer ")
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
|
@ -139,7 +150,6 @@ func main() {
|
|||
}
|
||||
}()
|
||||
|
||||
fmt.Println("Waiting for an exit message")
|
||||
isStartGame := <-exit
|
||||
if isStartGame != "" {
|
||||
fmt.Println("Connecting to game", isStartGame)
|
||||
|
@ -157,6 +167,7 @@ func main() {
|
|||
}
|
||||
|
||||
func ConnectToLobby(username string) (net.Conn, error) {
|
||||
slog.Debug("connecting to server...")
|
||||
conn, err := net.Dial("tcp", "127.0.0.1:12345")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Sorry, failed to connect to server...")
|
||||
|
|
|
@ -5,7 +5,11 @@ import (
|
|||
"log"
|
||||
"log/slog"
|
||||
"net"
|
||||
"os"
|
||||
"sshpong/internal/config"
|
||||
"sshpong/internal/lobby"
|
||||
"sshpong/internal/pong"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
|
@ -13,14 +17,20 @@ var exit chan bool
|
|||
var games sync.Map
|
||||
|
||||
func main() {
|
||||
slog.SetLogLoggerLevel(slog.LevelDebug)
|
||||
if len(os.Args) == 1 {
|
||||
config.LoadConfig("")
|
||||
} else {
|
||||
config.LoadConfig(os.Args[1])
|
||||
}
|
||||
|
||||
slog.SetLogLoggerLevel(slog.Level(config.Config.LogLevel))
|
||||
fmt.Println("Starting sshpong lobby...")
|
||||
go LobbyListen()
|
||||
fmt.Println("Lobby started")
|
||||
|
||||
// fmt.Println("Starting game listener...")
|
||||
// go GamesListen()
|
||||
// fmt.Println("Game listener started")
|
||||
fmt.Println("Starting game listener...")
|
||||
go GamesListen()
|
||||
fmt.Println("Game listener started")
|
||||
|
||||
_ = <-exit
|
||||
}
|
||||
|
@ -28,10 +38,9 @@ func main() {
|
|||
// Starts listening on port 12345 for TCP connections
|
||||
// Also creates client pool and game connection singletons
|
||||
func LobbyListen() {
|
||||
|
||||
listener, err := net.Listen("tcp", "127.0.0.1:12345")
|
||||
if err != nil {
|
||||
slog.Error("Error setting up listener for lobby. Exiting...")
|
||||
slog.Error("Error setting up listener for lobby. Exiting...", err)
|
||||
}
|
||||
|
||||
defer listener.Close()
|
||||
|
@ -64,60 +73,62 @@ func LobbyListen() {
|
|||
}
|
||||
}
|
||||
|
||||
// func GamesListen() {
|
||||
//
|
||||
// slog.SetLogLoggerLevel(slog.LevelDebug)
|
||||
// slog.Debug("Debug level logs are active")
|
||||
//
|
||||
// gameListener, err := net.Listen("tcp", "127.0.0.1:42069")
|
||||
// if err != nil {
|
||||
// log.Fatal(err)
|
||||
// }
|
||||
//
|
||||
// for {
|
||||
// defer gameListener.Close()
|
||||
// conn, err := gameListener.Accept()
|
||||
// if err != nil {
|
||||
// log.Println(err)
|
||||
// continue
|
||||
// }
|
||||
//
|
||||
// slog.Debug("Received game connection")
|
||||
//
|
||||
// go func(conn net.Conn) {
|
||||
// messageBytes := make([]byte, 126)
|
||||
//
|
||||
// n, err := conn.Read(messageBytes)
|
||||
// if err != nil {
|
||||
// log.Printf("Error reading game ID on connection %s", err)
|
||||
// }
|
||||
//
|
||||
// gInfo := strings.SplitAfter(string(messageBytes[:n]), ":")
|
||||
// if err != nil {
|
||||
// log.Printf("Game id was not a string? %s", err)
|
||||
// }
|
||||
//
|
||||
// slog.Debug("Game request data", slog.Any("game info", gInfo))
|
||||
//
|
||||
// game, ok := games.Load(gInfo[0])
|
||||
// if !ok {
|
||||
// games.Store(gInfo[0], GameClients{Client1: Client{
|
||||
// Username: gInfo[1],
|
||||
// Conn: conn,
|
||||
// }, Client2: Client{}})
|
||||
// } else {
|
||||
// gameclients, _ := game.(GameClients)
|
||||
// client2 := Client{
|
||||
// Username: gInfo[1],
|
||||
// Conn: conn,
|
||||
// }
|
||||
//
|
||||
// games.Store(gInfo[0], GameClients{
|
||||
// Client1: gameclients.Client1,
|
||||
// Client2: client2})
|
||||
//
|
||||
// go pong.StartGame(gameclients.Client1.Conn, client2.Conn, gameclients.Client1.Username, client2.Username)
|
||||
// }
|
||||
// }(conn)
|
||||
// }
|
||||
// }
|
||||
func GamesListen() {
|
||||
|
||||
type GameClients struct {
|
||||
Client1 lobby.Client
|
||||
Client2 lobby.Client
|
||||
}
|
||||
|
||||
gameListener, err := net.Listen("tcp", "127.0.0.1:42069")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
for {
|
||||
defer gameListener.Close()
|
||||
conn, err := gameListener.Accept()
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
continue
|
||||
}
|
||||
|
||||
slog.Debug("Received game connection")
|
||||
|
||||
go func(conn net.Conn) {
|
||||
messageBytes := make([]byte, 126)
|
||||
|
||||
n, err := conn.Read(messageBytes)
|
||||
if err != nil {
|
||||
log.Printf("Error reading game ID on connection %s", err)
|
||||
}
|
||||
|
||||
gInfo := strings.SplitAfter(string(messageBytes[:n]), ":")
|
||||
if err != nil {
|
||||
log.Printf("Game id was not a string? %s", err)
|
||||
}
|
||||
|
||||
slog.Debug("Game request data", slog.Any("game info", gInfo))
|
||||
|
||||
game, ok := games.Load(gInfo[0])
|
||||
if !ok {
|
||||
games.Store(gInfo[0], GameClients{Client1: lobby.Client{
|
||||
Username: gInfo[1],
|
||||
Conn: conn,
|
||||
}, Client2: lobby.Client{}})
|
||||
} else {
|
||||
gameclients, _ := game.(GameClients)
|
||||
client2 := lobby.Client{
|
||||
Username: gInfo[1],
|
||||
Conn: conn,
|
||||
}
|
||||
|
||||
games.Store(gInfo[0], GameClients{
|
||||
Client1: gameclients.Client1,
|
||||
Client2: client2})
|
||||
|
||||
go pong.StartGame(gameclients.Client1.Conn, client2.Conn, gameclients.Client1.Username, client2.Username)
|
||||
}
|
||||
}(conn)
|
||||
}
|
||||
}
|
||||
|
|
3
config.json
Normal file
3
config.json
Normal file
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"logLevel": 1
|
||||
}
|
7
go.mod
7
go.mod
|
@ -1,11 +1,12 @@
|
|||
module sshpong
|
||||
|
||||
go 1.22.2
|
||||
go 1.23.1
|
||||
|
||||
require (
|
||||
github.com/google/uuid v1.6.0
|
||||
golang.org/x/exp v0.0.0-20240823005443-9b4947da3948
|
||||
golang.org/x/sys v0.23.0 // indirect
|
||||
golang.org/x/term v0.22.0 // indirect
|
||||
golang.org/x/term v0.22.0
|
||||
google.golang.org/protobuf v1.34.2
|
||||
)
|
||||
|
||||
require golang.org/x/sys v0.23.0 // indirect
|
||||
|
|
2
go.sum
2
go.sum
|
@ -1,3 +1,5 @@
|
|||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
golang.org/x/exp v0.0.0-20240823005443-9b4947da3948 h1:kx6Ds3MlpiUHKj7syVnbp57++8WpuKPcR5yjLBjvLEA=
|
||||
|
|
|
@ -2,6 +2,8 @@ package ansii
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"math"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
|
@ -37,8 +39,8 @@ const (
|
|||
)
|
||||
|
||||
type Offset struct {
|
||||
X int
|
||||
Y int
|
||||
X float32
|
||||
Y float32
|
||||
}
|
||||
|
||||
type style struct {
|
||||
|
@ -89,8 +91,8 @@ func RestoreTerm(prev *term.State) error {
|
|||
return term.Restore(fd, prev)
|
||||
}
|
||||
|
||||
func (s screen) PlaceCursor(offset Offset) ANSI {
|
||||
return ANSI(fmt.Sprintf("\033[%d;%dH", offset.Y, offset.X))
|
||||
func (s screen) PlaceCursor(X, Y int) ANSI {
|
||||
return ANSI(fmt.Sprintf("\033[%d;%dH", Y, X))
|
||||
}
|
||||
|
||||
var (
|
||||
|
@ -105,31 +107,34 @@ var (
|
|||
// Blocks that would be placed off screen are clipped.
|
||||
func DrawBox(builder *strings.Builder, offset Offset, height int, width int, style ANSI) {
|
||||
builder.WriteString(string(style))
|
||||
for hIdx := 0; hIdx < height; hIdx++ {
|
||||
|
||||
for hIdx := range height {
|
||||
if hIdx == 0 || hIdx == height-1 {
|
||||
for wIdx := 0; wIdx < width; wIdx++ {
|
||||
DrawPixel(builder, Offset{X: offset.X + wIdx, Y: offset.Y + hIdx})
|
||||
for wIdx := range width {
|
||||
drawPixel(builder, offset.X+float32(wIdx), offset.Y+float32(hIdx))
|
||||
}
|
||||
} else {
|
||||
DrawPixel(builder, Offset{X: offset.X, Y: offset.Y + hIdx})
|
||||
DrawPixel(builder, Offset{X: offset.X + width - 1, Y: offset.Y + hIdx})
|
||||
drawPixel(builder, offset.X, offset.Y+float32(hIdx))
|
||||
drawPixel(builder, offset.X+float32(width-1), offset.Y+float32(hIdx))
|
||||
}
|
||||
}
|
||||
builder.WriteString(string(Styles.Reset))
|
||||
return
|
||||
}
|
||||
|
||||
func DrawPixel(builder *strings.Builder, offset Offset) {
|
||||
func drawPixel(builder *strings.Builder, offsetX, offsetY float32) {
|
||||
termWidth, termHeight := GetTermSize()
|
||||
if offset.X > termWidth || offset.Y > termHeight || offset.X < 0 || offset.Y < 0 {
|
||||
return
|
||||
}
|
||||
builder.WriteString(string(Screen.PlaceCursor(offset) + ANSI(Blocks.Block)))
|
||||
|
||||
scaledX := math.Floor(float64(offsetX * float32(termWidth)))
|
||||
scaledY := math.Floor(float64(offsetY * float32(termHeight)))
|
||||
|
||||
slog.Debug("positions", slog.Any("x", scaledX), slog.Any("y", scaledY))
|
||||
|
||||
// TODO: Does float to int convertion cause many problems?
|
||||
builder.WriteString(string(Screen.PlaceCursor(int(scaledX), int(scaledY)) + ANSI(Blocks.Block)))
|
||||
}
|
||||
|
||||
func DrawPixelStyle(builder *strings.Builder, offset Offset, style ANSI) {
|
||||
builder.WriteString(string(style))
|
||||
DrawPixel(builder, offset)
|
||||
drawPixel(builder, offset.X, offset.Y)
|
||||
builder.WriteString(string(Styles.Reset))
|
||||
}
|
||||
|
|
|
@ -7,6 +7,8 @@ import (
|
|||
"log/slog"
|
||||
"sshpong/internal/lobby"
|
||||
"strings"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type InterrupterMessage struct {
|
||||
|
@ -16,8 +18,8 @@ type InterrupterMessage struct {
|
|||
|
||||
var help = fmt.Errorf("use invite <player name> to invite a player\nchat or / to send a message to the lobby\nq or quit to leave the game")
|
||||
|
||||
var red = "\x1b[31m"
|
||||
var normal = "\033[0m"
|
||||
var Red = "\x1b[31m"
|
||||
var Normal = "\033[0m"
|
||||
|
||||
func HandleUserInput(args []string, username string) ([]byte, error) {
|
||||
if len(args) == 0 {
|
||||
|
@ -26,11 +28,15 @@ func HandleUserInput(args []string, username string) ([]byte, error) {
|
|||
switch args[0] {
|
||||
case "invite":
|
||||
if args[1] != "" {
|
||||
msg, err := lobby.Marshal(lobby.InviteData{From: username, To: args[1]}, lobby.Invite)
|
||||
if err != nil {
|
||||
slog.Debug("invite message was not properly marshalled", "error", err)
|
||||
if args[1] == username {
|
||||
fmt.Println("You cannot invite yourself to a game ;)")
|
||||
} else {
|
||||
msg, err := lobby.Marshal(lobby.InviteData{From: username, To: args[1]}, lobby.Invite)
|
||||
if err != nil {
|
||||
slog.Debug("invite message was not properly marshalled", "error", err)
|
||||
}
|
||||
return msg, err
|
||||
}
|
||||
return msg, err
|
||||
} else {
|
||||
fmt.Println("Please provide a player to invite ")
|
||||
}
|
||||
|
@ -84,13 +90,15 @@ func HandleInterruptInput(incoming InterrupterMessage, args []string, username s
|
|||
switch incoming.InterruptType {
|
||||
// Respond with yes if you accept game
|
||||
case "invite":
|
||||
slog.Debug("handling invite interrupt")
|
||||
if len(args) < 1 {
|
||||
return []byte{}, nil
|
||||
} else {
|
||||
if strings.ToLower(args[0]) == "y" || strings.ToLower(args[0]) == "yes" {
|
||||
msg, err := lobby.Marshal(lobby.AcceptData{
|
||||
From: username,
|
||||
To: incoming.Content,
|
||||
From: username,
|
||||
To: incoming.Content,
|
||||
GameID: uuid.NewString(),
|
||||
}, lobby.Accept)
|
||||
if err != nil {
|
||||
slog.Debug("accept message was not properly marshalled", "error", err)
|
||||
|
@ -99,20 +107,21 @@ func HandleInterruptInput(incoming InterrupterMessage, args []string, username s
|
|||
}
|
||||
}
|
||||
|
||||
// Disconnect and connect to game
|
||||
case "accepted":
|
||||
msg, err := lobby.Marshal(lobby.DisconnectData{
|
||||
From: incoming.Content,
|
||||
}, lobby.Disconnect)
|
||||
if err != nil {
|
||||
slog.Debug("disconnect message was not properly marshalled", "error", err)
|
||||
}
|
||||
return msg, err
|
||||
// TODO: Do we need this accepted? Disconnect and connect to game
|
||||
// case "accepted":
|
||||
// msg, err := lobby.Marshal(lobby.DisconnectData{
|
||||
// From: incoming.Content,
|
||||
// }, lobby.Disconnect)
|
||||
// if err != nil {
|
||||
// slog.Debug("disconnect message was not properly marshalled", "error", err)
|
||||
// }
|
||||
// return msg, err
|
||||
|
||||
case "start_game":
|
||||
msg, err := lobby.Marshal(lobby.StartGameData{
|
||||
To: "",
|
||||
GameID: incoming.Content,
|
||||
}, lobby.Chat)
|
||||
}, lobby.StartGame)
|
||||
if err != nil {
|
||||
slog.Debug("start game message was not properly marshalled", "error", err)
|
||||
}
|
||||
|
@ -124,7 +133,6 @@ func HandleInterruptInput(incoming InterrupterMessage, args []string, username s
|
|||
|
||||
func HandleServerMessage(msg []byte) (InterrupterMessage, error) {
|
||||
header := msg[0]
|
||||
|
||||
switch header {
|
||||
case lobby.Invite:
|
||||
imsg, err := lobby.Unmarshal[lobby.InviteData](msg)
|
||||
|
@ -160,6 +168,7 @@ func HandleServerMessage(msg []byte) (InterrupterMessage, error) {
|
|||
if err != nil {
|
||||
return InterrupterMessage{}, errors.New("Not a properly formatted start game message")
|
||||
}
|
||||
fmt.Println("Your invite was accepted. Press Enter to join game")
|
||||
return InterrupterMessage{
|
||||
InterruptType: "start_game",
|
||||
Content: sgmsg.GameID,
|
||||
|
@ -206,7 +215,7 @@ func HandleServerMessage(msg []byte) (InterrupterMessage, error) {
|
|||
if err != nil {
|
||||
slog.Debug("Received an indecipherable error message...", slog.Any("msg", msg[1:]))
|
||||
}
|
||||
fmt.Println(red, em.Message, normal)
|
||||
fmt.Println(Red, em.Message, Normal)
|
||||
|
||||
}
|
||||
return InterrupterMessage{}, nil
|
||||
|
|
|
@ -166,9 +166,9 @@ func handleGameInput(bytes []byte) {
|
|||
// Up
|
||||
case 'w':
|
||||
if isPlayer1 {
|
||||
state.Player1.Pos.Y = state.Player1.Pos.Y + 1
|
||||
state.Player1.Pos.Y = state.Player1.Pos.Y - 1
|
||||
v, err := json.Marshal(pong.Vector{
|
||||
X: 0, Y: state.Player1.Pos.Y,
|
||||
X: -50, Y: state.Player1.Pos.Y,
|
||||
})
|
||||
if err != nil {
|
||||
slog.Debug("error marshalling player movement", slog.Any("error", err))
|
||||
|
@ -180,9 +180,9 @@ func handleGameInput(bytes []byte) {
|
|||
|
||||
egress <- update
|
||||
} else {
|
||||
state.Player2.Pos.Y = state.Player2.Pos.Y + 1
|
||||
state.Player2.Pos.Y = state.Player2.Pos.Y - 1
|
||||
v, err := json.Marshal(pong.Vector{
|
||||
X: 0, Y: state.Player2.Pos.Y,
|
||||
X: 50, Y: state.Player2.Pos.Y,
|
||||
})
|
||||
if err != nil {
|
||||
slog.Debug("error marshalling Player2 movement", slog.Any("error", err))
|
||||
|
@ -197,9 +197,9 @@ func handleGameInput(bytes []byte) {
|
|||
// Down
|
||||
case 's':
|
||||
if isPlayer1 {
|
||||
state.Player1.Pos.Y = state.Player1.Pos.Y - 1
|
||||
state.Player1.Pos.Y = state.Player1.Pos.Y + 1
|
||||
v, err := json.Marshal(pong.Vector{
|
||||
X: 0, Y: state.Player1.Pos.Y,
|
||||
X: -50, Y: state.Player1.Pos.Y,
|
||||
})
|
||||
if err != nil {
|
||||
slog.Debug("error marshalling player movement", slog.Any("error", err))
|
||||
|
@ -211,9 +211,9 @@ func handleGameInput(bytes []byte) {
|
|||
|
||||
egress <- update
|
||||
} else {
|
||||
state.Player2.Pos.Y = state.Player2.Pos.Y - 1
|
||||
state.Player2.Pos.Y = state.Player2.Pos.Y + 1
|
||||
v, err := json.Marshal(pong.Vector{
|
||||
X: 0, Y: state.Player2.Pos.Y,
|
||||
X: 50, Y: state.Player2.Pos.Y,
|
||||
})
|
||||
if err != nil {
|
||||
slog.Debug("error marshalling player movement", slog.Any("error", err))
|
||||
|
@ -252,9 +252,9 @@ func handleGameInput(bytes []byte) {
|
|||
// Up
|
||||
case 65:
|
||||
if isPlayer1 {
|
||||
state.Player1.Pos.Y = state.Player1.Pos.Y + 1
|
||||
state.Player1.Pos.Y = state.Player1.Pos.Y - 1
|
||||
v, err := json.Marshal(pong.Vector{
|
||||
X: 0, Y: state.Player1.Pos.Y,
|
||||
X: -50, Y: state.Player1.Pos.Y,
|
||||
})
|
||||
if err != nil {
|
||||
slog.Debug("error marshalling player movement", slog.Any("error", err))
|
||||
|
@ -266,9 +266,9 @@ func handleGameInput(bytes []byte) {
|
|||
|
||||
egress <- update
|
||||
} else {
|
||||
state.Player2.Pos.Y = state.Player2.Pos.Y + 1
|
||||
state.Player2.Pos.Y = state.Player2.Pos.Y - 1
|
||||
v, err := json.Marshal(pong.Vector{
|
||||
X: 0, Y: state.Player2.Pos.Y,
|
||||
X: 50, Y: state.Player2.Pos.Y,
|
||||
})
|
||||
if err != nil {
|
||||
slog.Debug("error marshalling Player2 movement", slog.Any("error", err))
|
||||
|
@ -283,9 +283,9 @@ func handleGameInput(bytes []byte) {
|
|||
// Down
|
||||
case 66:
|
||||
if isPlayer1 {
|
||||
state.Player1.Pos.Y = state.Player1.Pos.Y - 1
|
||||
state.Player1.Pos.Y = state.Player1.Pos.Y + 1
|
||||
v, err := json.Marshal(pong.Vector{
|
||||
X: 0, Y: state.Player1.Pos.Y,
|
||||
X: -50, Y: state.Player1.Pos.Y,
|
||||
})
|
||||
if err != nil {
|
||||
slog.Debug("error marshalling player movement", slog.Any("error", err))
|
||||
|
@ -297,9 +297,9 @@ func handleGameInput(bytes []byte) {
|
|||
|
||||
egress <- update
|
||||
} else {
|
||||
state.Player2.Pos.Y = state.Player2.Pos.Y - 1
|
||||
state.Player2.Pos.Y = state.Player2.Pos.Y + 1
|
||||
v, err := json.Marshal(pong.Vector{
|
||||
X: 0, Y: state.Player2.Pos.Y,
|
||||
X: 50, Y: state.Player2.Pos.Y,
|
||||
})
|
||||
if err != nil {
|
||||
slog.Debug("error marshalling player movement", slog.Any("error", err))
|
||||
|
|
36
internal/config/config.go
Normal file
36
internal/config/config.go
Normal file
|
@ -0,0 +1,36 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"log/slog"
|
||||
"os"
|
||||
)
|
||||
|
||||
var Config Configuration
|
||||
|
||||
type Configuration struct {
|
||||
LogLevel int `json:"logLevel"`
|
||||
}
|
||||
|
||||
func LoadConfig(path string) {
|
||||
var c = Configuration{}
|
||||
|
||||
var cf []byte
|
||||
var err error
|
||||
if path != "" {
|
||||
cf, err = os.ReadFile(path)
|
||||
} else {
|
||||
cf, err = os.ReadFile("config.json")
|
||||
}
|
||||
if err != nil {
|
||||
slog.Info("failed to open config at path provided, using default config instead")
|
||||
}
|
||||
|
||||
err = json.Unmarshal(cf, &c)
|
||||
if err != nil {
|
||||
slog.Info("failed to read configuration, using default config instead...", err)
|
||||
}
|
||||
|
||||
Config = c
|
||||
return
|
||||
}
|
|
@ -6,8 +6,6 @@ import (
|
|||
"log/slog"
|
||||
"net"
|
||||
"sync"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type Lobby struct {
|
||||
|
@ -37,6 +35,7 @@ func CreateLobby() *Lobby {
|
|||
go func(lm *sync.Map) {
|
||||
for {
|
||||
msg := <-externalMessageChan
|
||||
slog.Debug("forwarding external message")
|
||||
|
||||
tc, ok := lm.Load(msg.Target)
|
||||
if !ok {
|
||||
|
@ -170,10 +169,17 @@ func (l *Lobby) handleClientLobbyMessage(msg []byte) ([]byte, error) {
|
|||
return []byte{}, err
|
||||
}
|
||||
|
||||
l.ExternalMessageChannel <- ExternalMessage{
|
||||
From: i.From,
|
||||
Target: i.To,
|
||||
Message: msg,
|
||||
_, ok := l.lobbyMembers.Load(i.To)
|
||||
if !ok {
|
||||
return Marshal(ErrorData{
|
||||
Message: fmt.Sprintf("Sorry, player %s is not available.", i.To),
|
||||
}, Error)
|
||||
} else {
|
||||
l.ExternalMessageChannel <- ExternalMessage{
|
||||
From: i.From,
|
||||
Target: i.To,
|
||||
Message: msg,
|
||||
}
|
||||
}
|
||||
|
||||
return Marshal(PendingInviteData{
|
||||
|
@ -195,12 +201,11 @@ func (l *Lobby) handleClientLobbyMessage(msg []byte) ([]byte, error) {
|
|||
return []byte{}, err
|
||||
}
|
||||
|
||||
gID := uuid.NewString()
|
||||
gID := a.GameID
|
||||
|
||||
msg, err := Marshal(AcceptedData{
|
||||
Accepter: a.From,
|
||||
GameID: gID,
|
||||
}, Accepted)
|
||||
msg, err := Marshal(StartGameData{
|
||||
GameID: gID,
|
||||
}, StartGame)
|
||||
|
||||
l.ExternalMessageChannel <- ExternalMessage{
|
||||
From: a.From,
|
||||
|
@ -208,23 +213,25 @@ func (l *Lobby) handleClientLobbyMessage(msg []byte) ([]byte, error) {
|
|||
Message: msg,
|
||||
}
|
||||
|
||||
slog.Debug("Sent start game message to inviter")
|
||||
|
||||
return Marshal(StartGameData{
|
||||
To: a.From,
|
||||
From: a.To,
|
||||
GameID: gID,
|
||||
}, StartGame)
|
||||
|
||||
case Accepted:
|
||||
a, err := Unmarshal[AcceptedData](msg)
|
||||
if err != nil {
|
||||
slog.Debug("error unmarshalling accpeted message", "error", err)
|
||||
return []byte{}, err
|
||||
}
|
||||
|
||||
// TODO: figure out the accepted and start game data situation... To field is a little hard to fill.
|
||||
return Marshal(StartGameData{
|
||||
To: "",
|
||||
GameID: a.GameID,
|
||||
}, StartGame)
|
||||
// TODO: figure out the accepted and start game data situation... To field is a little hard to fill.
|
||||
// case Accepted:
|
||||
// a, err := Unmarshal[AcceptedData](msg)
|
||||
// if err != nil {
|
||||
// slog.Debug("error unmarshalling accpeted message", "error", err)
|
||||
// return []byte{}, err
|
||||
// }
|
||||
// return Marshal(StartGameData{
|
||||
// To: "",
|
||||
// GameID: a.GameID,
|
||||
// }, StartGame)
|
||||
|
||||
// TODO: Like pending invite, I think start game is only a client message
|
||||
// case StartGame:
|
||||
|
|
|
@ -36,18 +36,20 @@ type PendingInviteData struct {
|
|||
}
|
||||
|
||||
type AcceptData struct {
|
||||
From string `json:"from"`
|
||||
To string `json:"to"`
|
||||
From string `json:"from"`
|
||||
To string `json:"to"`
|
||||
GameID string `json:"gameID"`
|
||||
}
|
||||
|
||||
type AcceptedData struct {
|
||||
Accepter string `json:"accepter"`
|
||||
GameID string `json:"game_id"`
|
||||
GameID string `json:"gameID"`
|
||||
}
|
||||
|
||||
type StartGameData struct {
|
||||
To string `json:"to"`
|
||||
GameID string `json:"game_id"`
|
||||
From string `json:"from"`
|
||||
GameID string `json:"gameID"`
|
||||
}
|
||||
|
||||
type DeclineData struct {
|
||||
|
|
|
@ -23,7 +23,7 @@ var player2 GameClient
|
|||
var ingress chan StateUpdate
|
||||
var egress chan StateUpdate
|
||||
|
||||
const posXBound = 100
|
||||
const posXBound = 52
|
||||
const negXBound = posXBound * -1
|
||||
const posYBound = 50
|
||||
const negYBound = posYBound * -1
|
||||
|
@ -141,6 +141,9 @@ func gameLoop(state *GameState) {
|
|||
return
|
||||
}
|
||||
ingress <- msg
|
||||
if msg.FieldPath == "Winner" {
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
|
@ -157,6 +160,9 @@ func gameLoop(state *GameState) {
|
|||
return
|
||||
}
|
||||
ingress <- msg
|
||||
if msg.FieldPath == "Winner" {
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
|
@ -164,6 +170,9 @@ func gameLoop(state *GameState) {
|
|||
for {
|
||||
msg := <-egress
|
||||
broadcastUpdate(msg)
|
||||
if msg.FieldPath == "Winner" {
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
|
@ -176,17 +185,23 @@ func gameLoop(state *GameState) {
|
|||
if err != nil {
|
||||
fmt.Println("FUCK!~", err)
|
||||
}
|
||||
if msg.FieldPath == "Winner" {
|
||||
slog.Debug("Closing game loop on winner message")
|
||||
return
|
||||
}
|
||||
|
||||
case _ = <-ticker.C:
|
||||
update := process(state)
|
||||
egress <- update
|
||||
if update.FieldPath == "Winner" {
|
||||
slog.Debug("Closing game loop")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func process(state *GameState) StateUpdate {
|
||||
|
||||
// Move players
|
||||
// Check if player edge is out of bounds
|
||||
// If out of bounds reset velocity to zero and position to edge
|
||||
|
@ -229,11 +244,13 @@ func process(state *GameState) StateUpdate {
|
|||
if state.Ball.Pos.X <= negXBound+1 && state.Ball.Vel.X < 0 {
|
||||
// Paddle hit!
|
||||
if state.Ball.Pos.Y <= state.Player1.Pos.Y+state.Player1.Size.Y && state.Ball.Pos.Y >= state.Player1.Pos.Y-state.Player1.Size.Y {
|
||||
slog.Debug("Player1 paddle hit!")
|
||||
state.Ball.Pos.X = (negXBound + 1) - (state.Ball.Pos.X - (negXBound + 1))
|
||||
state.Ball.Vel.X = state.Ball.Vel.X * -1.001
|
||||
angleTweak := (state.Ball.Pos.Y - state.Player2.Pos.Y) / (state.Player2.Size.Y / 2)
|
||||
state.Ball.Vel.Y = state.Ball.Vel.Y * angleTweak
|
||||
angleTweak := (state.Ball.Pos.Y - state.Player1.Pos.Y) / (state.Player1.Size.Y / 2)
|
||||
state.Ball.Vel.Y = angleTweak / 10
|
||||
} else {
|
||||
slog.Debug("Player1 paddle miss...")
|
||||
state.Ball.Pos.X = 0
|
||||
state.Ball.Pos.Y = 0
|
||||
state.Ball.Vel.X = 1
|
||||
|
@ -252,11 +269,13 @@ func process(state *GameState) StateUpdate {
|
|||
if state.Ball.Pos.X > posXBound-1 && state.Ball.Vel.X > 0 {
|
||||
// Paddle hit!
|
||||
if state.Ball.Pos.Y <= state.Player2.Pos.Y+state.Player2.Size.Y && state.Ball.Pos.Y >= state.Player2.Pos.Y-state.Player2.Size.Y {
|
||||
slog.Debug("Player2 paddle hit!")
|
||||
state.Ball.Pos.X = (posXBound - 1) - (state.Ball.Pos.X - (posXBound - 1))
|
||||
state.Ball.Vel.X = state.Ball.Vel.X * -1.001
|
||||
angleTweak := (state.Ball.Pos.Y - state.Player2.Pos.Y) / (state.Player2.Size.Y / 2)
|
||||
state.Ball.Vel.Y = state.Ball.Vel.Y * angleTweak
|
||||
state.Ball.Vel.Y = angleTweak / 10
|
||||
} else {
|
||||
slog.Debug("Player2 paddle miss...")
|
||||
state.Ball.Pos.X = 0
|
||||
state.Ball.Pos.Y = 0
|
||||
state.Ball.Vel.X = -1
|
||||
|
|
|
@ -6,7 +6,8 @@ import (
|
|||
"sshpong/internal/ansii"
|
||||
"sshpong/internal/pong"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"golang.org/x/term"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -15,96 +16,88 @@ var (
|
|||
millisecondTimeFrame float64 = float64(1 / targetFpMilli)
|
||||
quit chan bool
|
||||
userInput chan rune
|
||||
playerX int = 10
|
||||
playerY int = 10
|
||||
)
|
||||
|
||||
const (
|
||||
reset string = "\033[0m"
|
||||
plain string = ""
|
||||
bold string = "\033[1m"
|
||||
underline string = "\033[4m"
|
||||
black string = "\033[30m"
|
||||
red string = "\033[31m"
|
||||
green string = "\033[32m"
|
||||
yellow string = "\033[33m"
|
||||
blue string = "\033[34m"
|
||||
purple string = "\033[35m"
|
||||
cyan string = "\033[36m"
|
||||
white string = "\033[37m"
|
||||
blackBg string = "\033[40m"
|
||||
redBg string = "\033[41m"
|
||||
greenBg string = "\033[42m"
|
||||
yellowBg string = "\033[43m"
|
||||
blueBg string = "\033[44m"
|
||||
purpleBg string = "\033[45m"
|
||||
cyanBg string = "\033[46m"
|
||||
whiteBg string = "\033[47m"
|
||||
clearScreen string = "\033[2J"
|
||||
hideCursor string = "\033[?25l"
|
||||
showCursor string = "\033[?25h"
|
||||
)
|
||||
|
||||
func Render(state pong.GameState) {
|
||||
// drawScreen(state)
|
||||
fmt.Print("\033c")
|
||||
fmt.Println("Player 1", state.Player1.Pos.X, state.Player1.Pos.Y)
|
||||
fmt.Println("Player 2", state.Player2.Pos.X, state.Player2.Pos.Y)
|
||||
}
|
||||
|
||||
func writeCheckerBoard(height int, width int, builder *strings.Builder) {
|
||||
for i := 0; i < height; i++ {
|
||||
for j := 0; j < width; j++ {
|
||||
if i%2 == 0 {
|
||||
if j%2 == 0 {
|
||||
builder.WriteString("█")
|
||||
} else {
|
||||
|
||||
builder.WriteString(" ")
|
||||
}
|
||||
} else {
|
||||
if j%2 == 0 {
|
||||
builder.WriteString(" ")
|
||||
} else {
|
||||
builder.WriteString("█")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func drawScreen(state pong.GameState) {
|
||||
// width := 100
|
||||
// height := 50
|
||||
// fmt.Println("Player 1", ((state.Player1.Pos.X+50)/100)*width, ((state.Player1.Pos.Y+50)/100)*height)
|
||||
// fmt.Println("Player 2", ((state.Player2.Pos.X+50)/100)*width, ((state.Player2.Pos.Y+50)/100)*height)
|
||||
// fmt.Println("Ball", ((state.Ball.Pos.X+50)/100)*width, ((state.Ball.Pos.Y+50)/100)*height)
|
||||
var builder = strings.Builder{}
|
||||
builder.WriteString(string(ansii.Screen.ClearScreen))
|
||||
ansii.DrawBox(&builder, ansii.Offset{X: int(state.Player1.Pos.X), Y: int(state.Player1.Pos.Y)}, 5, 1, ansii.Colors.Cyan)
|
||||
ansii.DrawPixelStyle(&builder, ansii.Offset{X: int(state.Player1.Pos.X), Y: int(state.Player1.Pos.Y)}, ansii.Colors.Purple)
|
||||
ansii.DrawPixelStyle(&builder, ansii.Offset{X: int(state.Player1.Pos.X), Y: int(state.Player1.Pos.Y) + 5}, ansii.Colors.Purple)
|
||||
ansii.DrawBox(&builder, ansii.Offset{X: int(state.Player2.Pos.X), Y: int(state.Player2.Pos.Y)}, 5, 1, ansii.Colors.Cyan)
|
||||
ansii.DrawPixelStyle(&builder, ansii.Offset{X: int(state.Player2.Pos.X), Y: int(state.Player2.Pos.Y)}, ansii.Colors.Purple)
|
||||
ansii.DrawPixelStyle(&builder, ansii.Offset{X: int(state.Player2.Pos.X), Y: int(state.Player2.Pos.Y) + 5}, ansii.Colors.Purple)
|
||||
// Quit instructions
|
||||
// builder.WriteString(string(ansii.Screen.PlaceCursor(ansii.Offset{X: 0, Y: height})))
|
||||
// builder.WriteString("q to quit")
|
||||
x1, y1 := transformToTermPos(state.Player1.Pos)
|
||||
builder.WriteString(renderBox(&builder, x1+2, y1, 2, 10, cyan))
|
||||
|
||||
x2, y2 := transformToTermPos(state.Player2.Pos)
|
||||
builder.WriteString(renderBox(&builder, x2, y2, 2, 10, purple))
|
||||
|
||||
xb, yb := transformToTermPos(state.Ball.Pos)
|
||||
builder.WriteString(renderPixel(&builder, xb, yb, red))
|
||||
|
||||
builder.WriteString(renderMessage(&builder, state.Message))
|
||||
|
||||
os.Stdout.WriteString(builder.String())
|
||||
}
|
||||
|
||||
func drawFrameStats(frameNum int, frameTimeMs float64) {
|
||||
width, height := ansii.GetTermSize()
|
||||
var spareTimeMilli = millisecondTimeFrame - frameTimeMs
|
||||
os.Stdout.WriteString(string(ansii.Screen.PlaceCursor(ansii.Offset{X: width - 12, Y: height - 2})))
|
||||
os.Stdout.WriteString(fmt.Sprintf("Frame #: %d", frameNum))
|
||||
os.Stdout.WriteString(string(ansii.Screen.PlaceCursor(ansii.Offset{X: width - 19, Y: height - 1})))
|
||||
os.Stdout.WriteString(fmt.Sprintf("Frame Time: %.4fms", frameTimeMs))
|
||||
os.Stdout.WriteString(string(ansii.Screen.PlaceCursor(ansii.Offset{X: width - 20, Y: height})))
|
||||
os.Stdout.WriteString(fmt.Sprintf("Spare Time: %.4fms", spareTimeMilli))
|
||||
func setCursorPos(x, y int) string {
|
||||
return fmt.Sprintf("\033[%d;%dH", y, x)
|
||||
}
|
||||
|
||||
func handleInput(rawInput rune) {
|
||||
action := ProcessInput(rawInput)
|
||||
width, height := ansii.GetTermSize()
|
||||
|
||||
switch action {
|
||||
case Quit:
|
||||
fmt.Println("Quitting...")
|
||||
close(quit)
|
||||
case Left, LeftArrow:
|
||||
playerX = max(playerX-1, 0)
|
||||
case Right, RightArrow:
|
||||
playerX = min(playerX+1, width)
|
||||
case Up, UpArrow:
|
||||
playerY = max(playerY-1, 0)
|
||||
case Down, DownArrow:
|
||||
playerY = min(playerY+1, height)
|
||||
case Unknown:
|
||||
default:
|
||||
os.Stdout.WriteString(string(ansii.Screen.PlaceCursor(ansii.Offset{X: 0, Y: height - 2})))
|
||||
os.Stdout.WriteString("Unrecognized Input: " + string(action))
|
||||
close(quit)
|
||||
}
|
||||
}
|
||||
|
||||
func waitForFpsLock(startMs float64) {
|
||||
for {
|
||||
var nowMs = float64(time.Now().UnixNano()) / 1_000_000.0
|
||||
if nowMs-startMs >= millisecondTimeFrame {
|
||||
break
|
||||
// Renders a box with center positioned at X,Y with specified width and height
|
||||
func renderBox(builder *strings.Builder, X, Y, width, height int, style string) string {
|
||||
str := ""
|
||||
for x := X - (width / 2); x < X+(width/2); x++ {
|
||||
for y := Y - (height / 2); y < Y+(height/2); y++ {
|
||||
str = str + (setCursorPos(x, y) + style + "█")
|
||||
}
|
||||
}
|
||||
|
||||
return str
|
||||
}
|
||||
|
||||
func renderPixel(builder *strings.Builder, x, y int, style string) string {
|
||||
return (setCursorPos(x, y) + style + "█")
|
||||
}
|
||||
|
||||
func renderMessage(builder *strings.Builder, message string) string {
|
||||
xm, xy := transformToTermPos(pong.Vector{X: 40, Y: 40})
|
||||
return (setCursorPos(xm, xy) + reset + message)
|
||||
}
|
||||
|
||||
// Returns state x and y positions with center origin and 50 by 50 area
|
||||
// to scaled, top-left origin coordinates for the user's terminal size.
|
||||
func transformToTermPos(vec pong.Vector) (int, int) {
|
||||
iwidth, iheight, _ := term.GetSize(int(os.Stdin.Fd()))
|
||||
width := float32(iwidth)
|
||||
height := float32(iheight)
|
||||
|
||||
ix := int(((vec.X + 50) / 100) * width)
|
||||
iy := int(((vec.Y + 50) / 100) * height)
|
||||
|
||||
return ix, iy
|
||||
}
|
||||
|
|
6
invite_proc.txt
Normal file
6
invite_proc.txt
Normal file
|
@ -0,0 +1,6 @@
|
|||
1. Inviter invites other player
|
||||
2. Recipent acceptes invite
|
||||
3. Recipent Sends Accept message with generated GameID, receives StartGame message
|
||||
3. Recipent handles StartGame message and connects to game instance
|
||||
4. Inviter recieves StartGame message with GameID
|
||||
5. Inviter connects to game instance
|
11
makefile
11
makefile
|
@ -1,2 +1,13 @@
|
|||
lint:
|
||||
golangci-lint run
|
||||
|
||||
server:
|
||||
air -c .air_server.toml
|
||||
|
||||
client:
|
||||
while true; do \
|
||||
go run cmd/client/main.go;\
|
||||
echo "Client has exited, restarting...";\
|
||||
sleep 2;\
|
||||
done
|
||||
|
||||
|
|
1
tmp/build-errors.log
Normal file
1
tmp/build-errors.log
Normal file
|
@ -0,0 +1 @@
|
|||
exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1
|
BIN
tmp/client/main
Executable file
BIN
tmp/client/main
Executable file
Binary file not shown.
BIN
tmp/server/main
Executable file
BIN
tmp/server/main
Executable file
Binary file not shown.
Loading…
Reference in New Issue
Block a user