2025-06-19 16:22:55 +03:00
|
|
|
package server
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bufio"
|
2025-06-20 16:26:39 +03:00
|
|
|
"context"
|
2025-06-19 16:22:55 +03:00
|
|
|
"net"
|
2025-06-20 16:26:39 +03:00
|
|
|
"sync"
|
2025-06-19 16:22:55 +03:00
|
|
|
"time"
|
|
|
|
|
2025-06-22 17:54:07 +03:00
|
|
|
"code.haedhutner.dev/mvv/LastMUD/internal/game"
|
2025-06-20 16:26:39 +03:00
|
|
|
"code.haedhutner.dev/mvv/LastMUD/internal/logging"
|
2025-06-19 16:22:55 +03:00
|
|
|
"github.com/google/uuid"
|
|
|
|
)
|
|
|
|
|
2025-06-20 16:26:39 +03:00
|
|
|
const MaxLastSeenTime = 120 * time.Second
|
2025-06-22 17:54:07 +03:00
|
|
|
const MaxEnqueuedInputMessages = 10
|
2025-06-20 16:26:39 +03:00
|
|
|
|
2025-06-19 16:22:55 +03:00
|
|
|
type Connection struct {
|
2025-06-20 16:26:39 +03:00
|
|
|
ctx context.Context
|
|
|
|
wg *sync.WaitGroup
|
|
|
|
|
2025-06-22 17:54:07 +03:00
|
|
|
server *Server
|
|
|
|
|
2025-06-19 16:22:55 +03:00
|
|
|
identity uuid.UUID
|
|
|
|
|
2025-06-20 16:26:39 +03:00
|
|
|
conn *net.TCPConn
|
|
|
|
lastSeen time.Time
|
2025-06-19 16:22:55 +03:00
|
|
|
|
2025-06-22 17:54:07 +03:00
|
|
|
inputChannel chan []byte
|
2025-06-19 16:22:55 +03:00
|
|
|
}
|
|
|
|
|
2025-06-22 17:54:07 +03:00
|
|
|
func CreateConnection(server *Server, conn *net.TCPConn, ctx context.Context, wg *sync.WaitGroup) (c *Connection) {
|
2025-06-20 16:26:39 +03:00
|
|
|
logging.Info("Connect: ", conn.RemoteAddr())
|
|
|
|
|
|
|
|
conn.SetKeepAlive(true)
|
|
|
|
conn.SetKeepAlivePeriod(1 * time.Second)
|
|
|
|
|
|
|
|
c = &Connection{
|
|
|
|
ctx: ctx,
|
|
|
|
wg: wg,
|
2025-06-22 17:54:07 +03:00
|
|
|
server: server,
|
2025-06-19 16:22:55 +03:00
|
|
|
identity: uuid.New(),
|
|
|
|
conn: conn,
|
2025-06-22 17:54:07 +03:00
|
|
|
inputChannel: make(chan []byte, MaxEnqueuedInputMessages),
|
2025-06-20 16:26:39 +03:00
|
|
|
lastSeen: time.Now(),
|
2025-06-19 16:22:55 +03:00
|
|
|
}
|
2025-06-20 16:26:39 +03:00
|
|
|
|
|
|
|
c.wg.Add(2)
|
|
|
|
go c.listen()
|
|
|
|
go c.checkAlive()
|
|
|
|
|
2025-06-22 17:54:07 +03:00
|
|
|
server.game.EnqueueEvent(game.CreatePlayerJoinEvent(c.Id()))
|
|
|
|
|
2025-06-20 16:26:39 +03:00
|
|
|
return
|
2025-06-19 16:22:55 +03:00
|
|
|
}
|
|
|
|
|
2025-06-22 17:54:07 +03:00
|
|
|
func (c *Connection) Id() uuid.UUID {
|
|
|
|
return c.identity
|
|
|
|
}
|
|
|
|
|
2025-06-20 16:26:39 +03:00
|
|
|
func (c *Connection) listen() {
|
|
|
|
defer c.wg.Done()
|
2025-06-19 16:22:55 +03:00
|
|
|
|
2025-06-20 16:26:39 +03:00
|
|
|
logging.Info("Listening on connection ", c.conn.RemoteAddr())
|
2025-06-19 16:22:55 +03:00
|
|
|
|
|
|
|
for {
|
2025-06-20 16:26:39 +03:00
|
|
|
c.conn.SetReadDeadline(time.Time{})
|
2025-06-19 16:22:55 +03:00
|
|
|
|
2025-06-22 17:54:07 +03:00
|
|
|
message, err := bufio.NewReader(c.conn).ReadBytes('\n')
|
2025-06-19 16:22:55 +03:00
|
|
|
|
|
|
|
if err != nil {
|
2025-06-20 16:26:39 +03:00
|
|
|
logging.Warn(err)
|
|
|
|
break
|
2025-06-19 16:22:55 +03:00
|
|
|
}
|
|
|
|
|
2025-06-22 17:54:07 +03:00
|
|
|
if len(c.inputChannel) == MaxEnqueuedInputMessages {
|
|
|
|
c.conn.Write([]byte("You have too many commands enqueued. Please wait until some are processed.\n"))
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2025-06-19 16:22:55 +03:00
|
|
|
c.inputChannel <- message
|
|
|
|
|
|
|
|
c.conn.Write([]byte(message))
|
2025-06-20 16:26:39 +03:00
|
|
|
|
|
|
|
c.lastSeen = time.Now()
|
2025-06-19 16:22:55 +03:00
|
|
|
}
|
2025-06-20 16:26:39 +03:00
|
|
|
}
|
2025-06-19 16:22:55 +03:00
|
|
|
|
2025-06-20 16:26:39 +03:00
|
|
|
func (c *Connection) checkAlive() {
|
|
|
|
defer c.wg.Done()
|
|
|
|
defer c.closeConnection()
|
|
|
|
|
|
|
|
for {
|
|
|
|
if c.shouldClose() {
|
2025-06-22 17:54:07 +03:00
|
|
|
c.Write([]byte("Server shutting down, bye bye!\r\n"))
|
2025-06-20 16:26:39 +03:00
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
if time.Since(c.lastSeen) > MaxLastSeenTime {
|
2025-06-22 17:54:07 +03:00
|
|
|
c.Write([]byte("You have been away for too long, bye bye!\r\n"))
|
2025-06-20 16:26:39 +03:00
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
_, err := c.conn.Write([]byte{0x00})
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
2025-06-19 16:22:55 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Connection) shouldClose() bool {
|
|
|
|
select {
|
2025-06-20 16:26:39 +03:00
|
|
|
case <-c.ctx.Done():
|
2025-06-19 16:22:55 +03:00
|
|
|
return true
|
|
|
|
default:
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-06-20 16:26:39 +03:00
|
|
|
func (c *Connection) closeConnection() {
|
2025-06-22 17:54:07 +03:00
|
|
|
close(c.inputChannel)
|
|
|
|
|
2025-06-20 16:26:39 +03:00
|
|
|
c.conn.Close()
|
|
|
|
|
2025-06-22 17:54:07 +03:00
|
|
|
c.server.game.EnqueueEvent(game.CreatePlayerLeaveEvent(c.Id()))
|
|
|
|
|
2025-06-20 16:26:39 +03:00
|
|
|
logging.Info("Disconnected: ", c.conn.RemoteAddr())
|
2025-06-19 16:22:55 +03:00
|
|
|
}
|
|
|
|
|
2025-06-22 17:54:07 +03:00
|
|
|
func (c *Connection) NextInput() (input []byte, err error) {
|
2025-06-19 16:22:55 +03:00
|
|
|
select {
|
|
|
|
case val := <-c.inputChannel:
|
|
|
|
return val, nil
|
|
|
|
default:
|
2025-06-22 17:54:07 +03:00
|
|
|
return nil, newInputEmptyError()
|
2025-06-19 16:22:55 +03:00
|
|
|
}
|
|
|
|
}
|
2025-06-20 16:26:39 +03:00
|
|
|
|
2025-06-22 17:54:07 +03:00
|
|
|
func (c *Connection) Write(output []byte) (err error) {
|
|
|
|
_, err = c.conn.Write(output)
|
2025-06-20 16:26:39 +03:00
|
|
|
return
|
|
|
|
}
|