Draft: big refactor

This commit is contained in:
2025-09-16 16:03:27 +08:00
parent f527230f1e
commit c4f2b0af25
42 changed files with 684 additions and 215 deletions

View File

@@ -20,12 +20,14 @@ func Tick(d time.Duration) tea.Cmd {
}
type Base struct {
client *resty.Client
client *resty.Client
username string
}
func NewBase(client *resty.Client) *Base {
return &Base{
client: client,
client: client,
username: "",
}
}

121
plays/game.go Normal file
View File

@@ -0,0 +1,121 @@
package plays
import (
"fmt"
"net/url"
"sync"
"gitea.konchin.com/ytshih/inp2025/game/server/wordle"
"gitea.konchin.com/ytshih/inp2025/game/types"
tea "github.com/charmbracelet/bubbletea"
"github.com/gorilla/websocket"
"github.com/vmihailenco/msgpack/v5"
)
type Game struct {
*Base
server url.URL
mutex sync.RWMutex
state types.WordleState
input string
}
func (m *Game) GetState() {
c, _, err := websocket.DefaultDialer.Dial(m.server.String(), nil)
if err != nil {
panic(fmt.Errorf("failed to dial, %w", err))
}
defer c.Close()
for {
_, b, err := c.ReadMessage()
switch err.(type) {
case nil:
var state types.WordleState
if err := msgpack.Unmarshal(b, &state); err != nil {
panic(fmt.Errorf("failed to unmarshal state, %w", err))
}
m.mutex.Lock()
m.state = state
m.mutex.Unlock()
case *websocket.CloseError:
return
default:
panic(fmt.Errorf("failed to read message, %w", err))
}
}
}
func NewGame(base *Base, server url.URL) *Game {
m := &Game{
Base: base,
server: server,
state: types.NewWordleState(),
input: "",
}
go m.GetState()
return m
}
func (m *Game) Init() tea.Cmd {
return tea.Batch(tea.ClearScreen)
}
func (m *Game) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
if _, cmd := m.Base.Update(msg); cmd != nil {
return m, cmd
}
switch msg := msg.(type) {
case tea.KeyMsg:
switch msg.Type {
case tea.KeyBackspace:
if len(m.input) > 0 {
m.input = m.input[:len(m.input)-1]
}
case tea.KeyEnter:
if len(m.input) == types.MaxLength {
return m, tea.Quit
}
case tea.KeyRunes:
if len(m.input) < types.MaxLength {
m.input = m.input + string(msg.Runes)
}
}
}
return m, nil
}
func (m *Game) View() string {
m.mutex.RLock()
ret := m.state.View()
m.mutex.RUnlock()
return ret
}
func (m *Game) Next(queue *[]*tea.Program) error {
if len(m.input) == types.MaxLength {
b, err := msgpack.Marshal(wordle.Operation{
Type: wordle.OperationTypeGuess,
Username: m.username,
})
if err != nil {
return err
}
_, err = m.Base.client.R().
SetBody(b).
Post("/api/guess")
if err != nil {
return err
}
*queue = append(*queue,
tea.NewProgram(NewGame(m.Base, m.server)))
}
return nil
}

View File

@@ -14,7 +14,7 @@ import (
)
var (
lobbyChoices = []string{"No-op", "Logout"}
lobbyChoices = []string{"No-op", "Create Room", "Logout"}
)
type Lobby struct {
@@ -25,18 +25,18 @@ type Lobby struct {
updateCh chan struct{}
users []models.UserStatus
rooms []models.Room
rooms []types.Room
}
func NewLobby(base *Base) *Lobby {
m := Lobby{
m := &Lobby{
Base: base,
choice: "",
cursor: 0,
updateCh: make(chan struct{}, 1),
}
return &m
return m
}
func updateLobbyInfo(m *Lobby) error {
@@ -58,7 +58,8 @@ func updateLobbyInfo(m *Lobby) error {
}
m.users = users
var rooms []models.Room
var rooms []types.Room
// TODO: scan rooms
_, err = m.Base.client.R().
SetResult(&rooms).
ForceContentType("application/json").
@@ -147,6 +148,9 @@ func (m *Lobby) Next(queue *[]*tea.Program) error {
case "No-op":
*queue = append(*queue,
tea.NewProgram(NewLobby(m.Base)))
case "Create Room":
*queue = append(*queue,
tea.NewProgram(NewRoomWaiting(m.Base)))
case "Logout":
*queue = append(*queue,
tea.NewProgram(NewLogout(m.Base)))

View File

@@ -125,6 +125,7 @@ func (m *Login) Next(queue *[]*tea.Program) error {
if resp.StatusCode() == http.StatusOK {
m.Base.client.
SetBasicAuth(username, password)
m.Base.username = username
*queue = append(*queue,
tea.NewProgram(NewLobby(m.Base)))
} else {

View File

@@ -123,17 +123,19 @@ func (m *Register) Next(queue *[]*tea.Program) error {
}).
Post("/auth/register")
switch resp.StatusCode() {
case http.StatusOK:
if err == nil && resp.StatusCode() == http.StatusOK {
*queue = append(*queue,
tea.NewProgram(NewLogin(m.Base)))
case http.StatusBadRequest:
*queue = append(*queue,
tea.NewProgram(NewRedirect("Username already exist")))
*queue = append(*queue,
tea.NewProgram(NewRegister(m.Base)))
case http.StatusInternalServerError:
return err
} else {
switch resp.StatusCode() {
case http.StatusBadRequest:
*queue = append(*queue,
tea.NewProgram(NewRedirect("Username already exist")))
*queue = append(*queue,
tea.NewProgram(NewRegister(m.Base)))
default:
return err
}
}
return nil

65
plays/roomWaiting.go Normal file
View File

@@ -0,0 +1,65 @@
package plays
import (
"strings"
"gitea.konchin.com/ytshih/inp2025/game/types"
tea "github.com/charmbracelet/bubbletea"
)
type RoomWaiting struct {
*Base
doneCh chan struct{}
}
func NewRoomWaiting(base *Base) *RoomWaiting {
m := &RoomWaiting{
base: base,
doneCh: make(chan struct{}),
}
return &m
}
func updateRoomWaitingInfo(m *RoomWaiting) error {
for {
select {
case <-m.doneCh:
return nil
}
}
}
func (m *RoomWaiting) Init() tea.Cmd {
go updateRoomWaitingInfo(m)
return tea.Sequence(tea.ClearScreen, Tick(refreshTick))
}
func (m *RoomWaiting) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
if _, cmd := m.Base.Update(msg); cmd != nil {
return m, cmd
}
switch msg := msg.(type) {
case types.TickMsg:
return m, Tick(refreshTick)
}
return m, nil
}
func (m *RoomWaiting) View() string {
var b strings.Builder
b.WriteString("waiting for user to join ...")
return b.String()
}
func (m *RoomWaiting) Next(queue *[]*tea.Program) error {
m.doneCh <- struct{}{}
switch m.choice {
case "":
*queue = append(*queue,
tea.NewProgram)
}
}