No longer blocks randomly
This commit is contained in:
parent
2f926d5457
commit
45abc33c7f
10 changed files with 345 additions and 83 deletions
47
internal/game/event.go
Normal file
47
internal/game/event.go
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
package game
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
type EventType int
|
||||||
|
|
||||||
|
const (
|
||||||
|
PlayerJoin EventType = iota
|
||||||
|
PlayerCommand
|
||||||
|
PlayerLeave
|
||||||
|
)
|
||||||
|
|
||||||
|
type GameEvent interface {
|
||||||
|
Type() EventType
|
||||||
|
Handle(game *LastMUDGame, delta time.Duration)
|
||||||
|
}
|
||||||
|
|
||||||
|
type EventBus struct {
|
||||||
|
events chan GameEvent
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateEventBus() *EventBus {
|
||||||
|
return &EventBus{
|
||||||
|
events: make(chan GameEvent, 10),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (eb *EventBus) HasNext() bool {
|
||||||
|
return len(eb.events) > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (eb *EventBus) Pop() (event GameEvent) {
|
||||||
|
select {
|
||||||
|
case event := <-eb.events:
|
||||||
|
return event
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (eb *EventBus) Push(event GameEvent) {
|
||||||
|
eb.events <- event
|
||||||
|
}
|
||||||
|
|
||||||
|
func (eb *EventBus) close() {
|
||||||
|
close(eb.events)
|
||||||
|
}
|
44
internal/game/events.go
Normal file
44
internal/game/events.go
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
package game
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
type PlayerJoinEvent struct {
|
||||||
|
connectionId uuid.UUID
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreatePlayerJoinEvent(connId uuid.UUID) *PlayerJoinEvent {
|
||||||
|
return &PlayerJoinEvent{
|
||||||
|
connectionId: connId,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pje *PlayerJoinEvent) Type() EventType {
|
||||||
|
return PlayerJoin
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pje *PlayerJoinEvent) Handle(game *LastMUDGame, delta time.Duration) {
|
||||||
|
game.world.AddPlayerToDefaultRoom(CreatePlayer(pje.connectionId, nil))
|
||||||
|
game.enqeueOutput(CreateOutput(pje.connectionId, []byte("Welcome to LastMUD\n")))
|
||||||
|
}
|
||||||
|
|
||||||
|
type PlayerLeaveEvent struct {
|
||||||
|
connectionId uuid.UUID
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreatePlayerLeaveEvent(connId uuid.UUID) *PlayerLeaveEvent {
|
||||||
|
return &PlayerLeaveEvent{
|
||||||
|
connectionId: connId,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ple *PlayerLeaveEvent) Type() EventType {
|
||||||
|
return PlayerJoin
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ple *PlayerLeaveEvent) Handle(game *LastMUDGame, delta time.Duration) {
|
||||||
|
game.world.RemovePlayerById(ple.connectionId.String())
|
||||||
|
}
|
|
@ -6,22 +6,49 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"code.haedhutner.dev/mvv/LastMUD/internal/logging"
|
"code.haedhutner.dev/mvv/LastMUD/internal/logging"
|
||||||
|
"github.com/google/uuid"
|
||||||
)
|
)
|
||||||
|
|
||||||
const TickRate = time.Duration(50 * time.Millisecond)
|
const TickRate = time.Duration(50 * time.Millisecond)
|
||||||
|
|
||||||
type GameSignal struct {
|
type GameOutput struct {
|
||||||
|
connId uuid.UUID
|
||||||
|
contents []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateOutput(connId uuid.UUID, contents []byte) GameOutput {
|
||||||
|
return GameOutput{
|
||||||
|
connId: connId,
|
||||||
|
contents: contents,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g GameOutput) Id() uuid.UUID {
|
||||||
|
return g.connId
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g GameOutput) Contents() []byte {
|
||||||
|
return g.contents
|
||||||
}
|
}
|
||||||
|
|
||||||
type LastMUDGame struct {
|
type LastMUDGame struct {
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
wg *sync.WaitGroup
|
wg *sync.WaitGroup
|
||||||
|
|
||||||
|
world *World
|
||||||
|
|
||||||
|
eventBus *EventBus
|
||||||
|
|
||||||
|
output chan GameOutput
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateGame(ctx context.Context, wg *sync.WaitGroup) (game *LastMUDGame) {
|
func CreateGame(ctx context.Context, wg *sync.WaitGroup) (game *LastMUDGame) {
|
||||||
game = &LastMUDGame{
|
game = &LastMUDGame{
|
||||||
wg: wg,
|
wg: wg,
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
|
eventBus: CreateEventBus(),
|
||||||
|
output: make(chan GameOutput, 10),
|
||||||
|
world: CreateWorld(),
|
||||||
}
|
}
|
||||||
|
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
|
@ -58,6 +85,8 @@ func (game *LastMUDGame) start() {
|
||||||
|
|
||||||
func (game *LastMUDGame) shutdown() {
|
func (game *LastMUDGame) shutdown() {
|
||||||
logging.Info("Stopping LastMUD...")
|
logging.Info("Stopping LastMUD...")
|
||||||
|
close(game.output)
|
||||||
|
game.eventBus.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (game *LastMUDGame) shouldStop() bool {
|
func (game *LastMUDGame) shouldStop() bool {
|
||||||
|
@ -69,7 +98,31 @@ func (game *LastMUDGame) shouldStop() bool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *LastMUDGame) tick(delta time.Duration) {
|
func (game *LastMUDGame) EnqueueEvent(event GameEvent) {
|
||||||
// logging.Debug("Tick")
|
game.eventBus.Push(event)
|
||||||
// TODO
|
}
|
||||||
|
|
||||||
|
func (game *LastMUDGame) enqeueOutput(output GameOutput) {
|
||||||
|
game.output <- output
|
||||||
|
}
|
||||||
|
|
||||||
|
func (game *LastMUDGame) ConsumeNextOutput() *GameOutput {
|
||||||
|
select {
|
||||||
|
case output := <-game.output:
|
||||||
|
return &output
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *LastMUDGame) tick(delta time.Duration) {
|
||||||
|
for {
|
||||||
|
event := g.eventBus.Pop()
|
||||||
|
|
||||||
|
if event == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
event.Handle(g, delta)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,19 +1,24 @@
|
||||||
package game
|
package game
|
||||||
|
|
||||||
|
import "github.com/google/uuid"
|
||||||
|
|
||||||
type Player struct {
|
type Player struct {
|
||||||
GameObject
|
id uuid.UUID
|
||||||
Name
|
|
||||||
Description
|
currentRoom *Room
|
||||||
Position
|
|
||||||
Velocity
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreatePlayer(name, description string, x, y int) *Player {
|
func CreatePlayer(identity uuid.UUID, room *Room) *Player {
|
||||||
return &Player{
|
return &Player{
|
||||||
GameObject: CreateGameObject(),
|
id: identity,
|
||||||
Name: WithName(name),
|
currentRoom: room,
|
||||||
Description: WithDescription(description),
|
|
||||||
Position: WithPosition(x, y),
|
|
||||||
Velocity: WithVelocity(0, 0),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *Player) Identity() string {
|
||||||
|
return p.id.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Player) SetRoom(r *Room) {
|
||||||
|
p.currentRoom = r
|
||||||
|
}
|
||||||
|
|
42
internal/game/room.go
Normal file
42
internal/game/room.go
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
package game
|
||||||
|
|
||||||
|
type RoomPlayer interface {
|
||||||
|
Identity() string
|
||||||
|
SetRoom(room *Room)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Room struct {
|
||||||
|
North *Room
|
||||||
|
South *Room
|
||||||
|
East *Room
|
||||||
|
West *Room
|
||||||
|
|
||||||
|
Name string
|
||||||
|
Description string
|
||||||
|
|
||||||
|
players map[string]RoomPlayer
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateRoom(name, description string) *Room {
|
||||||
|
return &Room{
|
||||||
|
Name: name,
|
||||||
|
Description: description,
|
||||||
|
players: map[string]RoomPlayer{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Room) PlayerJoinRoom(player RoomPlayer) (err error) {
|
||||||
|
r.players[player.Identity()] = player
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Room) PlayerLeaveRoom(player RoomPlayer) (err error) {
|
||||||
|
delete(r.players, player.Identity())
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Room) Players() map[string]RoomPlayer {
|
||||||
|
return r.players
|
||||||
|
}
|
|
@ -1,45 +1 @@
|
||||||
package game
|
package game
|
||||||
|
|
||||||
import "github.com/google/uuid"
|
|
||||||
|
|
||||||
type GameObject struct {
|
|
||||||
uuid uuid.UUID
|
|
||||||
}
|
|
||||||
|
|
||||||
func CreateGameObject() GameObject {
|
|
||||||
return GameObject{
|
|
||||||
uuid: uuid.New(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type Position struct {
|
|
||||||
x, y int
|
|
||||||
}
|
|
||||||
|
|
||||||
func WithPosition(x, y int) Position {
|
|
||||||
return Position{x, y}
|
|
||||||
}
|
|
||||||
|
|
||||||
type Velocity struct {
|
|
||||||
velX, velY int
|
|
||||||
}
|
|
||||||
|
|
||||||
func WithVelocity(velX, velY int) Velocity {
|
|
||||||
return Velocity{velX, velY}
|
|
||||||
}
|
|
||||||
|
|
||||||
type Name struct {
|
|
||||||
name string
|
|
||||||
}
|
|
||||||
|
|
||||||
func WithName(name string) Name {
|
|
||||||
return Name{name}
|
|
||||||
}
|
|
||||||
|
|
||||||
type Description struct {
|
|
||||||
description string
|
|
||||||
}
|
|
||||||
|
|
||||||
func WithDescription(description string) Description {
|
|
||||||
return Description{description}
|
|
||||||
}
|
|
||||||
|
|
64
internal/game/world.go
Normal file
64
internal/game/world.go
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
package game
|
||||||
|
|
||||||
|
type World struct {
|
||||||
|
rooms []*Room
|
||||||
|
players map[string]*Player
|
||||||
|
defaultRoom *Room
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateWorld() *World {
|
||||||
|
forest := CreateRoom("Forest", "A dense, misty forest stretches endlessly, its towering trees whispering secrets through rustling leaves. Sunbeams filter through the canopy, dappling the mossy ground with golden light.")
|
||||||
|
cabin := CreateRoom("Wooden Cabin", "The cabin’s interior is cozy and rustic, with wooden beams overhead and a stone fireplace crackling warmly. A wool rug lies on creaky floorboards, and shelves brim with books, mugs, and old lanterns.")
|
||||||
|
lake := CreateRoom("Ethermere Lake", "Ethermire Lake lies shrouded in mist, its dark, still waters reflecting a sky perpetually overcast. Whispers ride the wind, and strange lights flicker beneath the surface, never breaking it.")
|
||||||
|
graveyard := CreateRoom("Graveyard", "An overgrown graveyard shrouded in fog, with cracked headstones and leaning statues. The wind sighs through dead trees, and unseen footsteps echo faintly among the mossy graves.")
|
||||||
|
chapel := CreateRoom("Chapel of the Hollow Light", "This ruined chapel leans under ivy and age. Faint light filters through shattered stained glass, casting broken rainbows across dust-choked pews and a long-silent altar.")
|
||||||
|
|
||||||
|
forest.North = cabin
|
||||||
|
forest.South = graveyard
|
||||||
|
forest.East = lake
|
||||||
|
forest.West = chapel
|
||||||
|
|
||||||
|
cabin.South = forest
|
||||||
|
cabin.West = chapel
|
||||||
|
cabin.East = lake
|
||||||
|
|
||||||
|
chapel.North = cabin
|
||||||
|
chapel.South = graveyard
|
||||||
|
chapel.East = forest
|
||||||
|
|
||||||
|
lake.West = forest
|
||||||
|
lake.North = cabin
|
||||||
|
lake.South = graveyard
|
||||||
|
|
||||||
|
graveyard.North = forest
|
||||||
|
graveyard.West = chapel
|
||||||
|
graveyard.East = lake
|
||||||
|
|
||||||
|
return &World{
|
||||||
|
rooms: []*Room{
|
||||||
|
forest,
|
||||||
|
cabin,
|
||||||
|
lake,
|
||||||
|
graveyard,
|
||||||
|
chapel,
|
||||||
|
},
|
||||||
|
defaultRoom: forest,
|
||||||
|
players: map[string]*Player{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *World) AddPlayerToDefaultRoom(p *Player) {
|
||||||
|
w.players[p.Identity()] = p
|
||||||
|
w.defaultRoom.PlayerJoinRoom(p)
|
||||||
|
p.SetRoom(w.defaultRoom)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *World) RemovePlayerById(id string) {
|
||||||
|
p, ok := w.players[id]
|
||||||
|
|
||||||
|
if ok {
|
||||||
|
p.currentRoom.PlayerLeaveRoom(p)
|
||||||
|
delete(w.players, id)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
|
@ -79,10 +79,16 @@ func CreateLogger(maxFileLevel LogLevel, maxDisplayedLevel LogLevel, filePath st
|
||||||
|
|
||||||
logFilePath := fmt.Sprintf("%s-%s%s", base, timestamp, ext) // "./base/dir/log-2006-01-02_15-04-05.txt"
|
logFilePath := fmt.Sprintf("%s-%s%s", base, timestamp, ext) // "./base/dir/log-2006-01-02_15-04-05.txt"
|
||||||
|
|
||||||
|
mkdirErr := os.MkdirAll(filepath.Dir(logFilePath), 0755)
|
||||||
|
|
||||||
|
if mkdirErr != nil {
|
||||||
|
err(os.Stdout, false, timestampFormat, mkdirErr)
|
||||||
|
}
|
||||||
|
|
||||||
file, fileErr := os.OpenFile(logFilePath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
|
file, fileErr := os.OpenFile(logFilePath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
|
||||||
|
|
||||||
if fileErr != nil {
|
if fileErr != nil {
|
||||||
err(os.Stdout, false, "Logging: Unable to write to file", filePath, fileErr)
|
err(os.Stdout, false, timestampFormat, fileErr)
|
||||||
} else {
|
} else {
|
||||||
logger.file = file
|
logger.file = file
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,25 +7,29 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"code.haedhutner.dev/mvv/LastMUD/internal/game"
|
||||||
"code.haedhutner.dev/mvv/LastMUD/internal/logging"
|
"code.haedhutner.dev/mvv/LastMUD/internal/logging"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
)
|
)
|
||||||
|
|
||||||
const MaxLastSeenTime = 120 * time.Second
|
const MaxLastSeenTime = 120 * time.Second
|
||||||
|
const MaxEnqueuedInputMessages = 10
|
||||||
|
|
||||||
type Connection struct {
|
type Connection struct {
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
wg *sync.WaitGroup
|
wg *sync.WaitGroup
|
||||||
|
|
||||||
|
server *Server
|
||||||
|
|
||||||
identity uuid.UUID
|
identity uuid.UUID
|
||||||
|
|
||||||
conn *net.TCPConn
|
conn *net.TCPConn
|
||||||
lastSeen time.Time
|
lastSeen time.Time
|
||||||
|
|
||||||
inputChannel chan string
|
inputChannel chan []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateConnection(conn *net.TCPConn, ctx context.Context, wg *sync.WaitGroup) (c *Connection) {
|
func CreateConnection(server *Server, conn *net.TCPConn, ctx context.Context, wg *sync.WaitGroup) (c *Connection) {
|
||||||
logging.Info("Connect: ", conn.RemoteAddr())
|
logging.Info("Connect: ", conn.RemoteAddr())
|
||||||
|
|
||||||
conn.SetKeepAlive(true)
|
conn.SetKeepAlive(true)
|
||||||
|
@ -34,9 +38,10 @@ func CreateConnection(conn *net.TCPConn, ctx context.Context, wg *sync.WaitGroup
|
||||||
c = &Connection{
|
c = &Connection{
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
wg: wg,
|
wg: wg,
|
||||||
|
server: server,
|
||||||
identity: uuid.New(),
|
identity: uuid.New(),
|
||||||
conn: conn,
|
conn: conn,
|
||||||
inputChannel: make(chan string),
|
inputChannel: make(chan []byte, MaxEnqueuedInputMessages),
|
||||||
lastSeen: time.Now(),
|
lastSeen: time.Now(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,9 +49,15 @@ func CreateConnection(conn *net.TCPConn, ctx context.Context, wg *sync.WaitGroup
|
||||||
go c.listen()
|
go c.listen()
|
||||||
go c.checkAlive()
|
go c.checkAlive()
|
||||||
|
|
||||||
|
server.game.EnqueueEvent(game.CreatePlayerJoinEvent(c.Id()))
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Connection) Id() uuid.UUID {
|
||||||
|
return c.identity
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Connection) listen() {
|
func (c *Connection) listen() {
|
||||||
defer c.wg.Done()
|
defer c.wg.Done()
|
||||||
|
|
||||||
|
@ -55,13 +66,18 @@ func (c *Connection) listen() {
|
||||||
for {
|
for {
|
||||||
c.conn.SetReadDeadline(time.Time{})
|
c.conn.SetReadDeadline(time.Time{})
|
||||||
|
|
||||||
message, err := bufio.NewReader(c.conn).ReadString('\n')
|
message, err := bufio.NewReader(c.conn).ReadBytes('\n')
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logging.Warn(err)
|
logging.Warn(err)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(c.inputChannel) == MaxEnqueuedInputMessages {
|
||||||
|
c.conn.Write([]byte("You have too many commands enqueued. Please wait until some are processed.\n"))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
c.inputChannel <- message
|
c.inputChannel <- message
|
||||||
|
|
||||||
c.conn.Write([]byte(message))
|
c.conn.Write([]byte(message))
|
||||||
|
@ -76,12 +92,12 @@ func (c *Connection) checkAlive() {
|
||||||
|
|
||||||
for {
|
for {
|
||||||
if c.shouldClose() {
|
if c.shouldClose() {
|
||||||
c.Write("Server shutting down, bye bye!\r\n")
|
c.Write([]byte("Server shutting down, bye bye!\r\n"))
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
if time.Since(c.lastSeen) > MaxLastSeenTime {
|
if time.Since(c.lastSeen) > MaxLastSeenTime {
|
||||||
c.Write("You have been away for too long, bye bye!\r\n")
|
c.Write([]byte("You have been away for too long, bye bye!\r\n"))
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -103,21 +119,25 @@ func (c *Connection) shouldClose() bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Connection) closeConnection() {
|
func (c *Connection) closeConnection() {
|
||||||
|
close(c.inputChannel)
|
||||||
|
|
||||||
c.conn.Close()
|
c.conn.Close()
|
||||||
|
|
||||||
|
c.server.game.EnqueueEvent(game.CreatePlayerLeaveEvent(c.Id()))
|
||||||
|
|
||||||
logging.Info("Disconnected: ", c.conn.RemoteAddr())
|
logging.Info("Disconnected: ", c.conn.RemoteAddr())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Connection) NextInput() (input string, err error) {
|
func (c *Connection) NextInput() (input []byte, err error) {
|
||||||
select {
|
select {
|
||||||
case val := <-c.inputChannel:
|
case val := <-c.inputChannel:
|
||||||
return val, nil
|
return val, nil
|
||||||
default:
|
default:
|
||||||
return "", newInputEmptyError()
|
return nil, newInputEmptyError()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Connection) Write(output string) (err error) {
|
func (c *Connection) Write(output []byte) (err error) {
|
||||||
_, err = c.conn.Write([]byte(output))
|
_, err = c.conn.Write(output)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
|
|
||||||
"code.haedhutner.dev/mvv/LastMUD/internal/game"
|
"code.haedhutner.dev/mvv/LastMUD/internal/game"
|
||||||
"code.haedhutner.dev/mvv/LastMUD/internal/logging"
|
"code.haedhutner.dev/mvv/LastMUD/internal/logging"
|
||||||
|
"github.com/google/uuid"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Server struct {
|
type Server struct {
|
||||||
|
@ -16,19 +17,19 @@ type Server struct {
|
||||||
|
|
||||||
listener *net.TCPListener
|
listener *net.TCPListener
|
||||||
|
|
||||||
connections []*Connection
|
connections map[uuid.UUID]*Connection
|
||||||
|
|
||||||
game *game.LastMUDGame
|
game *game.LastMUDGame
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateServer(ctx context.Context, wg *sync.WaitGroup, port string) (srv *Server, err error) {
|
func CreateServer(ctx context.Context, wg *sync.WaitGroup, port string) (srv *Server, err error) {
|
||||||
|
|
||||||
logging.Info(" _ _ __ __ _ _ ____ ")
|
logging.Info(" _ _ __ __ _ _ ____")
|
||||||
logging.Info(" | | __ _ ___| |_| \\/ | | | | _ \\ ")
|
logging.Info("| | __ _ ___| |_| \\/ | | | | _ \\")
|
||||||
logging.Info(" | | / _` / __| __| |\\/| | | | | | | | ")
|
logging.Info("| | / _` / __| __| |\\/| | | | | | | |")
|
||||||
logging.Info(" | |__| (_| \\__ \\ |_| | | | |_| | |_| | ")
|
logging.Info("| |__| (_| \\__ \\ |_| | | | |_| | |_| |")
|
||||||
logging.Info(" |_____\\__,_|___/\\__|_| |_|\\___/|____/ ")
|
logging.Info("|_____\\__,_|___/\\__|_| |_|\\___/|____/")
|
||||||
logging.Info(" ")
|
logging.Info("")
|
||||||
|
|
||||||
addr, err := net.ResolveTCPAddr("tcp", port)
|
addr, err := net.ResolveTCPAddr("tcp", port)
|
||||||
|
|
||||||
|
@ -50,13 +51,14 @@ func CreateServer(ctx context.Context, wg *sync.WaitGroup, port string) (srv *Se
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
wg: wg,
|
wg: wg,
|
||||||
listener: ln,
|
listener: ln,
|
||||||
connections: []*Connection{},
|
connections: map[uuid.UUID]*Connection{},
|
||||||
}
|
}
|
||||||
|
|
||||||
srv.game = game.CreateGame(ctx, srv.wg)
|
srv.game = game.CreateGame(ctx, srv.wg)
|
||||||
|
|
||||||
srv.wg.Add(1)
|
srv.wg.Add(2)
|
||||||
go srv.listen()
|
go srv.listen()
|
||||||
|
go srv.consumeGameOutput()
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -85,8 +87,31 @@ func (srv *Server) listen() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
c := CreateConnection(tcpConn, srv.ctx, srv.wg)
|
c := CreateConnection(srv, tcpConn, srv.ctx, srv.wg)
|
||||||
srv.connections = append(srv.connections, c)
|
|
||||||
|
srv.connections[c.Id()] = c
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (srv *Server) consumeGameOutput() {
|
||||||
|
defer srv.wg.Done()
|
||||||
|
|
||||||
|
for {
|
||||||
|
if srv.shouldStop() {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
output := srv.game.ConsumeNextOutput()
|
||||||
|
|
||||||
|
if output == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
conn, ok := srv.connections[output.Id()]
|
||||||
|
|
||||||
|
if ok {
|
||||||
|
conn.Write(output.Contents())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue