Basic chatting
This commit is contained in:
parent
45abc33c7f
commit
b8d8127bc0
14 changed files with 224 additions and 97 deletions
|
@ -12,22 +12,12 @@ func CreateCommand(cmdDef CommandDefinition, parameters []Parameter) Command {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cmd Command) Execute() (err error) {
|
func (cmd Command) Definition() CommandDefinition {
|
||||||
return cmd.commandDefinition.work(cmd.params...)
|
return cmd.commandDefinition
|
||||||
}
|
}
|
||||||
|
|
||||||
type commandContextError struct {
|
func (cmd Command) Parameters() []Parameter {
|
||||||
err string
|
return cmd.params
|
||||||
}
|
|
||||||
|
|
||||||
func createCommandContextError(err string) *commandContextError {
|
|
||||||
return &commandContextError{
|
|
||||||
err: err,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cce *commandContextError) Error() string {
|
|
||||||
return cce.err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type CommandContext struct {
|
type CommandContext struct {
|
||||||
|
@ -65,6 +55,6 @@ func CreateCommandContext(commandRegistry *CommandRegistry, commandString string
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ctx *CommandContext) ExecuteCommand() (err error) {
|
func (ctx *CommandContext) Command() Command {
|
||||||
return ctx.command.Execute()
|
return ctx.command
|
||||||
}
|
}
|
|
@ -17,3 +17,17 @@ func createCommandError(cmdName string, msg string, msgArgs ...any) *commandErro
|
||||||
func (cmdErr *commandError) Error() string {
|
func (cmdErr *commandError) Error() string {
|
||||||
return "Error with command '" + cmdErr.cmdName + "': " + cmdErr.message
|
return "Error with command '" + cmdErr.cmdName + "': " + cmdErr.message
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type commandContextError struct {
|
||||||
|
err string
|
||||||
|
}
|
||||||
|
|
||||||
|
func createCommandContextError(err string) *commandContextError {
|
||||||
|
return &commandContextError{
|
||||||
|
err: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cce *commandContextError) Error() string {
|
||||||
|
return cce.err
|
||||||
|
}
|
|
@ -1,6 +1,8 @@
|
||||||
package command
|
package command
|
||||||
|
|
||||||
import "log"
|
import (
|
||||||
|
"code.haedhutner.dev/mvv/LastMUD/internal/logging"
|
||||||
|
)
|
||||||
|
|
||||||
type TokenMatcher func(tokens []Token) bool
|
type TokenMatcher func(tokens []Token) bool
|
||||||
|
|
||||||
|
@ -12,20 +14,17 @@ type CommandDefinition struct {
|
||||||
name string
|
name string
|
||||||
tokenMatcher TokenMatcher
|
tokenMatcher TokenMatcher
|
||||||
parameterParser ParameterParser
|
parameterParser ParameterParser
|
||||||
work CommandWork
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateCommandDefinition(
|
func CreateCommandDefinition(
|
||||||
name string,
|
name string,
|
||||||
tokenMatcher TokenMatcher,
|
tokenMatcher TokenMatcher,
|
||||||
parameterParser ParameterParser,
|
parameterParser ParameterParser,
|
||||||
work CommandWork,
|
|
||||||
) CommandDefinition {
|
) CommandDefinition {
|
||||||
return CommandDefinition{
|
return CommandDefinition{
|
||||||
name: name,
|
name: name,
|
||||||
tokenMatcher: tokenMatcher,
|
tokenMatcher: tokenMatcher,
|
||||||
parameterParser: parameterParser,
|
parameterParser: parameterParser,
|
||||||
work: work,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,10 +40,6 @@ func (def CommandDefinition) ParseParameters(tokens []Token) []Parameter {
|
||||||
return def.parameterParser(tokens)
|
return def.parameterParser(tokens)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (def CommandDefinition) ExecuteFunc() CommandWork {
|
|
||||||
return def.work
|
|
||||||
}
|
|
||||||
|
|
||||||
type CommandRegistry struct {
|
type CommandRegistry struct {
|
||||||
commandDefinitions []CommandDefinition
|
commandDefinitions []CommandDefinition
|
||||||
}
|
}
|
||||||
|
@ -55,14 +50,10 @@ func CreateCommandRegistry(commandDefinitions ...CommandDefinition) *CommandRegi
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (comReg *CommandRegistry) Register(newCommandDefinitions ...CommandDefinition) {
|
|
||||||
comReg.commandDefinitions = append(comReg.commandDefinitions, newCommandDefinitions...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (comReg *CommandRegistry) Match(tokens []Token) (comDef *CommandDefinition) {
|
func (comReg *CommandRegistry) Match(tokens []Token) (comDef *CommandDefinition) {
|
||||||
for _, v := range comReg.commandDefinitions {
|
for _, v := range comReg.commandDefinitions {
|
||||||
if v.Match(tokens) {
|
if v.Match(tokens) {
|
||||||
log.Println("Found match", v.Name())
|
logging.Debug("Found match", v.Name())
|
||||||
return &v
|
return &v
|
||||||
}
|
}
|
||||||
}
|
}
|
31
internal/game/commands.go
Normal file
31
internal/game/commands.go
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
package game
|
||||||
|
|
||||||
|
import "code.haedhutner.dev/mvv/LastMUD/internal/game/command"
|
||||||
|
|
||||||
|
type CommandType = string
|
||||||
|
|
||||||
|
const (
|
||||||
|
SayCommand CommandType = "say"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (game *LastMUDGame) CreateGameCommandRegistry() *command.CommandRegistry {
|
||||||
|
return command.CreateCommandRegistry(
|
||||||
|
command.CreateCommandDefinition(
|
||||||
|
SayCommand,
|
||||||
|
func(tokens []command.Token) bool {
|
||||||
|
return len(tokens) > 1 && tokens[0].Lexeme() == "say"
|
||||||
|
},
|
||||||
|
func(tokens []command.Token) []command.Parameter {
|
||||||
|
saying := ""
|
||||||
|
|
||||||
|
for _, t := range tokens[1:] {
|
||||||
|
saying += t.Lexeme()
|
||||||
|
}
|
||||||
|
|
||||||
|
return []command.Parameter{
|
||||||
|
command.CreateParameter(saying),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
|
@ -1,6 +1,10 @@
|
||||||
package game
|
package game
|
||||||
|
|
||||||
import "time"
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"code.haedhutner.dev/mvv/LastMUD/internal/logging"
|
||||||
|
)
|
||||||
|
|
||||||
type EventType int
|
type EventType int
|
||||||
|
|
||||||
|
@ -8,6 +12,8 @@ const (
|
||||||
PlayerJoin EventType = iota
|
PlayerJoin EventType = iota
|
||||||
PlayerCommand
|
PlayerCommand
|
||||||
PlayerLeave
|
PlayerLeave
|
||||||
|
|
||||||
|
PlayerSpeak
|
||||||
)
|
)
|
||||||
|
|
||||||
type GameEvent interface {
|
type GameEvent interface {
|
||||||
|
@ -32,6 +38,7 @@ func (eb *EventBus) HasNext() bool {
|
||||||
func (eb *EventBus) Pop() (event GameEvent) {
|
func (eb *EventBus) Pop() (event GameEvent) {
|
||||||
select {
|
select {
|
||||||
case event := <-eb.events:
|
case event := <-eb.events:
|
||||||
|
logging.Info("Popped event of type ", event.Type(), ":", event)
|
||||||
return event
|
return event
|
||||||
default:
|
default:
|
||||||
return nil
|
return nil
|
||||||
|
@ -40,6 +47,7 @@ func (eb *EventBus) Pop() (event GameEvent) {
|
||||||
|
|
||||||
func (eb *EventBus) Push(event GameEvent) {
|
func (eb *EventBus) Push(event GameEvent) {
|
||||||
eb.events <- event
|
eb.events <- event
|
||||||
|
logging.Info("Enqueued event of type ", event.Type(), ":", event)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (eb *EventBus) close() {
|
func (eb *EventBus) close() {
|
||||||
|
|
|
@ -3,6 +3,8 @@ package game
|
||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"code.haedhutner.dev/mvv/LastMUD/internal/game/command"
|
||||||
|
"code.haedhutner.dev/mvv/LastMUD/internal/logging"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -10,7 +12,7 @@ type PlayerJoinEvent struct {
|
||||||
connectionId uuid.UUID
|
connectionId uuid.UUID
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreatePlayerJoinEvent(connId uuid.UUID) *PlayerJoinEvent {
|
func (game *LastMUDGame) CreatePlayerJoinEvent(connId uuid.UUID) *PlayerJoinEvent {
|
||||||
return &PlayerJoinEvent{
|
return &PlayerJoinEvent{
|
||||||
connectionId: connId,
|
connectionId: connId,
|
||||||
}
|
}
|
||||||
|
@ -22,23 +24,90 @@ func (pje *PlayerJoinEvent) Type() EventType {
|
||||||
|
|
||||||
func (pje *PlayerJoinEvent) Handle(game *LastMUDGame, delta time.Duration) {
|
func (pje *PlayerJoinEvent) Handle(game *LastMUDGame, delta time.Duration) {
|
||||||
game.world.AddPlayerToDefaultRoom(CreatePlayer(pje.connectionId, nil))
|
game.world.AddPlayerToDefaultRoom(CreatePlayer(pje.connectionId, nil))
|
||||||
game.enqeueOutput(CreateOutput(pje.connectionId, []byte("Welcome to LastMUD\n")))
|
game.enqeueOutput(game.CreateOutput(pje.connectionId, []byte("Welcome to LastMUD\n")))
|
||||||
}
|
}
|
||||||
|
|
||||||
type PlayerLeaveEvent struct {
|
type PlayerLeaveEvent struct {
|
||||||
connectionId uuid.UUID
|
connectionId uuid.UUID
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreatePlayerLeaveEvent(connId uuid.UUID) *PlayerLeaveEvent {
|
func (game *LastMUDGame) CreatePlayerLeaveEvent(connId uuid.UUID) *PlayerLeaveEvent {
|
||||||
return &PlayerLeaveEvent{
|
return &PlayerLeaveEvent{
|
||||||
connectionId: connId,
|
connectionId: connId,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ple *PlayerLeaveEvent) Type() EventType {
|
func (ple *PlayerLeaveEvent) Type() EventType {
|
||||||
return PlayerJoin
|
return PlayerLeave
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ple *PlayerLeaveEvent) Handle(game *LastMUDGame, delta time.Duration) {
|
func (ple *PlayerLeaveEvent) Handle(game *LastMUDGame, delta time.Duration) {
|
||||||
game.world.RemovePlayerById(ple.connectionId.String())
|
game.world.RemovePlayerById(ple.connectionId)
|
||||||
|
}
|
||||||
|
|
||||||
|
type PlayerCommandEvent struct {
|
||||||
|
connectionId uuid.UUID
|
||||||
|
command *command.CommandContext
|
||||||
|
}
|
||||||
|
|
||||||
|
func (game *LastMUDGame) CreatePlayerCommandEvent(connId uuid.UUID, cmdString string) (event *PlayerCommandEvent, err error) {
|
||||||
|
cmdCtx, err := command.CreateCommandContext(game.CommandRegistry(), cmdString)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
event = &PlayerCommandEvent{
|
||||||
|
connectionId: connId,
|
||||||
|
command: cmdCtx,
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pce *PlayerCommandEvent) Type() EventType {
|
||||||
|
return PlayerCommand
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pce *PlayerCommandEvent) Handle(game *LastMUDGame, delta time.Duration) {
|
||||||
|
player := game.world.FindPlayerById(pce.connectionId)
|
||||||
|
|
||||||
|
if player == nil {
|
||||||
|
logging.Error("Unable to handle player command from player with id", pce.connectionId, ": Player does not exist")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch pce.command.Command().Definition().Name() {
|
||||||
|
case SayCommand:
|
||||||
|
speech, err := pce.command.Command().Parameters()[0].AsString()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
logging.Error("Unable to handle player speech from player with id", pce.connectionId, ": Speech could not be parsed: ", err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
game.EnqueueEvent(game.CreatePlayerSayEvent(player, speech))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type PlayerSayEvent struct {
|
||||||
|
player *Player
|
||||||
|
speech string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (game *LastMUDGame) CreatePlayerSayEvent(player *Player, speech string) *PlayerSayEvent {
|
||||||
|
return &PlayerSayEvent{
|
||||||
|
player: player,
|
||||||
|
speech: speech,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pse *PlayerSayEvent) Type() EventType {
|
||||||
|
return PlayerSpeak
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pse *PlayerSayEvent) Handle(game *LastMUDGame, delta time.Duration) {
|
||||||
|
for _, p := range pse.player.CurrentRoom().Players() {
|
||||||
|
game.enqeueOutput(game.CreateOutput(p.Identity(), []byte(pse.player.id.String()+" in "+pse.player.CurrentRoom().Name+": "+pse.speech)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"code.haedhutner.dev/mvv/LastMUD/internal/game/command"
|
||||||
"code.haedhutner.dev/mvv/LastMUD/internal/logging"
|
"code.haedhutner.dev/mvv/LastMUD/internal/logging"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
)
|
)
|
||||||
|
@ -16,7 +17,7 @@ type GameOutput struct {
|
||||||
contents []byte
|
contents []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateOutput(connId uuid.UUID, contents []byte) GameOutput {
|
func (game *LastMUDGame) CreateOutput(connId uuid.UUID, contents []byte) GameOutput {
|
||||||
return GameOutput{
|
return GameOutput{
|
||||||
connId: connId,
|
connId: connId,
|
||||||
contents: contents,
|
contents: contents,
|
||||||
|
@ -35,6 +36,7 @@ type LastMUDGame struct {
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
wg *sync.WaitGroup
|
wg *sync.WaitGroup
|
||||||
|
|
||||||
|
commandRegistry *command.CommandRegistry
|
||||||
world *World
|
world *World
|
||||||
|
|
||||||
eventBus *EventBus
|
eventBus *EventBus
|
||||||
|
@ -51,6 +53,8 @@ func CreateGame(ctx context.Context, wg *sync.WaitGroup) (game *LastMUDGame) {
|
||||||
world: CreateWorld(),
|
world: CreateWorld(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
game.commandRegistry = game.CreateGameCommandRegistry()
|
||||||
|
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go game.start()
|
go game.start()
|
||||||
|
|
||||||
|
@ -115,6 +119,10 @@ func (game *LastMUDGame) ConsumeNextOutput() *GameOutput {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (game *LastMUDGame) CommandRegistry() *command.CommandRegistry {
|
||||||
|
return game.commandRegistry
|
||||||
|
}
|
||||||
|
|
||||||
func (g *LastMUDGame) tick(delta time.Duration) {
|
func (g *LastMUDGame) tick(delta time.Duration) {
|
||||||
for {
|
for {
|
||||||
event := g.eventBus.Pop()
|
event := g.eventBus.Pop()
|
||||||
|
|
|
@ -15,10 +15,14 @@ func CreatePlayer(identity uuid.UUID, room *Room) *Player {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Player) Identity() string {
|
func (p *Player) Identity() uuid.UUID {
|
||||||
return p.id.String()
|
return p.id
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Player) SetRoom(r *Room) {
|
func (p *Player) SetRoom(r *Room) {
|
||||||
p.currentRoom = r
|
p.currentRoom = r
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *Player) CurrentRoom() *Room {
|
||||||
|
return p.currentRoom
|
||||||
|
}
|
||||||
|
|
|
@ -1,11 +1,15 @@
|
||||||
package game
|
package game
|
||||||
|
|
||||||
|
import "github.com/google/uuid"
|
||||||
|
|
||||||
type RoomPlayer interface {
|
type RoomPlayer interface {
|
||||||
Identity() string
|
Identity() uuid.UUID
|
||||||
SetRoom(room *Room)
|
SetRoom(room *Room)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Room struct {
|
type Room struct {
|
||||||
|
world *World
|
||||||
|
|
||||||
North *Room
|
North *Room
|
||||||
South *Room
|
South *Room
|
||||||
East *Room
|
East *Room
|
||||||
|
@ -14,14 +18,15 @@ type Room struct {
|
||||||
Name string
|
Name string
|
||||||
Description string
|
Description string
|
||||||
|
|
||||||
players map[string]RoomPlayer
|
players map[uuid.UUID]RoomPlayer
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateRoom(name, description string) *Room {
|
func CreateRoom(world *World, name, description string) *Room {
|
||||||
return &Room{
|
return &Room{
|
||||||
|
world: world,
|
||||||
Name: name,
|
Name: name,
|
||||||
Description: description,
|
Description: description,
|
||||||
players: map[string]RoomPlayer{},
|
players: map[uuid.UUID]RoomPlayer{},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,6 +42,10 @@ func (r *Room) PlayerLeaveRoom(player RoomPlayer) (err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Room) Players() map[string]RoomPlayer {
|
func (r *Room) Players() map[uuid.UUID]RoomPlayer {
|
||||||
return r.players
|
return r.players
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *Room) World() *World {
|
||||||
|
return r.world
|
||||||
|
}
|
||||||
|
|
|
@ -1,17 +1,23 @@
|
||||||
package game
|
package game
|
||||||
|
|
||||||
|
import "github.com/google/uuid"
|
||||||
|
|
||||||
type World struct {
|
type World struct {
|
||||||
rooms []*Room
|
rooms []*Room
|
||||||
players map[string]*Player
|
players map[uuid.UUID]*Player
|
||||||
defaultRoom *Room
|
defaultRoom *Room
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateWorld() *World {
|
func CreateWorld() (world *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.")
|
world = &World{
|
||||||
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.")
|
players: map[uuid.UUID]*Player{},
|
||||||
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 := CreateRoom(world, "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(world, "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(world, "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(world, "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(world, "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.North = cabin
|
||||||
forest.South = graveyard
|
forest.South = graveyard
|
||||||
|
@ -34,17 +40,17 @@ func CreateWorld() *World {
|
||||||
graveyard.West = chapel
|
graveyard.West = chapel
|
||||||
graveyard.East = lake
|
graveyard.East = lake
|
||||||
|
|
||||||
return &World{
|
world.rooms = []*Room{
|
||||||
rooms: []*Room{
|
|
||||||
forest,
|
forest,
|
||||||
cabin,
|
cabin,
|
||||||
lake,
|
lake,
|
||||||
graveyard,
|
graveyard,
|
||||||
chapel,
|
chapel,
|
||||||
},
|
|
||||||
defaultRoom: forest,
|
|
||||||
players: map[string]*Player{},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
world.defaultRoom = forest
|
||||||
|
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *World) AddPlayerToDefaultRoom(p *Player) {
|
func (w *World) AddPlayerToDefaultRoom(p *Player) {
|
||||||
|
@ -53,7 +59,7 @@ func (w *World) AddPlayerToDefaultRoom(p *Player) {
|
||||||
p.SetRoom(w.defaultRoom)
|
p.SetRoom(w.defaultRoom)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *World) RemovePlayerById(id string) {
|
func (w *World) RemovePlayerById(id uuid.UUID) {
|
||||||
p, ok := w.players[id]
|
p, ok := w.players[id]
|
||||||
|
|
||||||
if ok {
|
if ok {
|
||||||
|
@ -62,3 +68,13 @@ func (w *World) RemovePlayerById(id string) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w *World) FindPlayerById(id uuid.UUID) *Player {
|
||||||
|
p, ok := w.players[id]
|
||||||
|
|
||||||
|
if ok {
|
||||||
|
return p
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -7,13 +7,11 @@ 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
|
||||||
|
@ -25,8 +23,6 @@ type Connection struct {
|
||||||
|
|
||||||
conn *net.TCPConn
|
conn *net.TCPConn
|
||||||
lastSeen time.Time
|
lastSeen time.Time
|
||||||
|
|
||||||
inputChannel chan []byte
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateConnection(server *Server, 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) {
|
||||||
|
@ -41,7 +37,6 @@ func CreateConnection(server *Server, conn *net.TCPConn, ctx context.Context, wg
|
||||||
server: server,
|
server: server,
|
||||||
identity: uuid.New(),
|
identity: uuid.New(),
|
||||||
conn: conn,
|
conn: conn,
|
||||||
inputChannel: make(chan []byte, MaxEnqueuedInputMessages),
|
|
||||||
lastSeen: time.Now(),
|
lastSeen: time.Now(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,7 +44,7 @@ func CreateConnection(server *Server, conn *net.TCPConn, ctx context.Context, wg
|
||||||
go c.listen()
|
go c.listen()
|
||||||
go c.checkAlive()
|
go c.checkAlive()
|
||||||
|
|
||||||
server.game.EnqueueEvent(game.CreatePlayerJoinEvent(c.Id()))
|
server.game().EnqueueEvent(server.game().CreatePlayerJoinEvent(c.Id()))
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -66,22 +61,21 @@ func (c *Connection) listen() {
|
||||||
for {
|
for {
|
||||||
c.conn.SetReadDeadline(time.Time{})
|
c.conn.SetReadDeadline(time.Time{})
|
||||||
|
|
||||||
message, err := bufio.NewReader(c.conn).ReadBytes('\n')
|
message, err := bufio.NewReader(c.conn).ReadString('\n')
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logging.Warn(err)
|
logging.Warn(err)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(c.inputChannel) == MaxEnqueuedInputMessages {
|
event, err := c.server.game().CreatePlayerCommandEvent(c.Id(), message)
|
||||||
c.conn.Write([]byte("You have too many commands enqueued. Please wait until some are processed.\n"))
|
|
||||||
continue
|
if err != nil {
|
||||||
|
c.conn.Write([]byte(err.Error() + "\n"))
|
||||||
|
} else {
|
||||||
|
c.server.game().EnqueueEvent(event)
|
||||||
}
|
}
|
||||||
|
|
||||||
c.inputChannel <- message
|
|
||||||
|
|
||||||
c.conn.Write([]byte(message))
|
|
||||||
|
|
||||||
c.lastSeen = time.Now()
|
c.lastSeen = time.Now()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -119,24 +113,13 @@ 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()))
|
c.server.game().EnqueueEvent(c.server.game().CreatePlayerLeaveEvent(c.Id()))
|
||||||
|
|
||||||
logging.Info("Disconnected: ", c.conn.RemoteAddr())
|
logging.Info("Disconnected: ", c.conn.RemoteAddr())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Connection) NextInput() (input []byte, err error) {
|
|
||||||
select {
|
|
||||||
case val := <-c.inputChannel:
|
|
||||||
return val, nil
|
|
||||||
default:
|
|
||||||
return nil, newInputEmptyError()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Connection) Write(output []byte) (err error) {
|
func (c *Connection) Write(output []byte) (err error) {
|
||||||
_, err = c.conn.Write(output)
|
_, err = c.conn.Write(output)
|
||||||
return
|
return
|
||||||
|
|
|
@ -19,7 +19,7 @@ type Server struct {
|
||||||
|
|
||||||
connections map[uuid.UUID]*Connection
|
connections map[uuid.UUID]*Connection
|
||||||
|
|
||||||
game *game.LastMUDGame
|
lastmudgame *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) {
|
||||||
|
@ -54,7 +54,7 @@ func CreateServer(ctx context.Context, wg *sync.WaitGroup, port string) (srv *Se
|
||||||
connections: map[uuid.UUID]*Connection{},
|
connections: map[uuid.UUID]*Connection{},
|
||||||
}
|
}
|
||||||
|
|
||||||
srv.game = game.CreateGame(ctx, srv.wg)
|
srv.lastmudgame = game.CreateGame(ctx, srv.wg)
|
||||||
|
|
||||||
srv.wg.Add(2)
|
srv.wg.Add(2)
|
||||||
go srv.listen()
|
go srv.listen()
|
||||||
|
@ -63,6 +63,10 @@ func CreateServer(ctx context.Context, wg *sync.WaitGroup, port string) (srv *Se
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (srv *Server) game() *game.LastMUDGame {
|
||||||
|
return srv.lastmudgame
|
||||||
|
}
|
||||||
|
|
||||||
func (srv *Server) listen() {
|
func (srv *Server) listen() {
|
||||||
defer srv.wg.Done()
|
defer srv.wg.Done()
|
||||||
defer srv.shutdown()
|
defer srv.shutdown()
|
||||||
|
@ -101,7 +105,7 @@ func (srv *Server) consumeGameOutput() {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
output := srv.game.ConsumeNextOutput()
|
output := srv.lastmudgame.ConsumeNextOutput()
|
||||||
|
|
||||||
if output == nil {
|
if output == nil {
|
||||||
continue
|
continue
|
||||||
|
|
Loading…
Add table
Reference in a new issue