package connection import ( "context" "log" "mchess_server/types" "sync" "github.com/google/uuid" gorillaws "github.com/gorilla/websocket" ) type Connection struct { ID uuid.UUID ws *gorillaws.Conn wsConnectionEstablished chan bool wsWriteLock sync.Mutex ctx context.Context buffer MessageBuffer disconnectCallback func() forColor types.ChessColor } func NewConnection(options ...func(*Connection)) *Connection { connection := Connection{ ID: uuid.New(), buffer: *newMessageBuffer(100), wsConnectionEstablished: make(chan bool), } for _, option := range options { option(&connection) } return &connection } func WithWebsocket(ws *gorillaws.Conn) func(*Connection) { return func(c *Connection) { c.ws = ws } } func WithContext(ctx context.Context) func(*Connection) { return func(c *Connection) { c.ctx = ctx } } func WithDisconnectCallback(cb func()) func(*Connection) { return func(c *Connection) { if cb != nil { c.disconnectCallback = cb } } } func (conn *Connection) SetForColor(color types.ChessColor) { conn.forColor = color } func (conn *Connection) SetDisconnectCallback(cb func()) { conn.disconnectCallback = cb } func (conn *Connection) HasWebsocketConnection() bool { return conn.ws != nil } func (conn *Connection) SetWebsocketConnection(ws *gorillaws.Conn) { if ws == nil { conn.logConnection("ERROR: setting ws = null") return } conn.ws = ws select { case conn.wsConnectionEstablished <- true: conn.logConnection("case wsConnectionEstablished <- true") default: conn.logConnection("DEFAULT CASE") } go func() { for { _, msg, err := conn.ws.ReadMessage() if err != nil { conn.logConnection("while reading from websocket: %w", err) conn.unsetWebsocketConnection() if conn.disconnectCallback != nil { conn.disconnectCallback() } return } conn.buffer.Insert(string(msg)) } }() defer conn.logConnection("websocket connection set") } func (conn *Connection) unsetWebsocketConnection() { conn.logConnection("websocket connection unset") conn.ws = nil } func (conn *Connection) Write(msg []byte) error { conn.logConnection("about to write") conn.logConnection("locking") conn.wsWriteLock.Lock() defer conn.logConnection("unlocking") defer conn.wsWriteLock.Unlock() if conn.ws == nil { //if ws is not yet set, we wait for it conn.logConnection("waiting for wsConnectionEstablished channel") <-conn.wsConnectionEstablished } conn.logConnection("Writing message: %s", string(msg)) return conn.ws.WriteMessage(gorillaws.TextMessage, msg) } func (conn *Connection) Read() ([]byte, error) { msg, err := conn.buffer.Get() if err != nil { conn.ws = nil return nil, err // TODO: Tell game-handler that connection was lost } return []byte(msg), err } func (conn *Connection) Close(msg string) { conn.logConnection("closing websocket connection") conn.ws.WriteMessage(gorillaws.TextMessage, []byte(msg)) conn.ws.Close() conn.ws = nil } func (con *Connection) logConnection(v ...any) { log.Println("on connection: ", con.ID) log.Println("for color: ", con.forColor.String(), v) }