ECS
This commit is contained in:
parent
fff70cc8b3
commit
87f5c2f842
14 changed files with 153 additions and 54 deletions
|
@ -7,6 +7,7 @@ import (
|
|||
"os/signal"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"code.haedhutner.dev/mvv/LastMUD/internal/server"
|
||||
|
||||
|
@ -57,5 +58,7 @@ func processInput() {
|
|||
if buf[0] == 'q' {
|
||||
return
|
||||
}
|
||||
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
|
|
BIN
internal/game/.DS_Store
vendored
Normal file
BIN
internal/game/.DS_Store
vendored
Normal file
Binary file not shown.
17
internal/game/db/repository.go
Normal file
17
internal/game/db/repository.go
Normal file
|
@ -0,0 +1,17 @@
|
|||
package db
|
||||
|
||||
import "github.com/google/uuid"
|
||||
|
||||
type Identifier = uuid.UUID
|
||||
|
||||
type Entity interface {
|
||||
Id() Identifier
|
||||
}
|
||||
|
||||
type Repository[T Entity] interface {
|
||||
Create(entity T) (rowsAffected int, err error)
|
||||
Delete(entity T) (rowsAffected int, err error)
|
||||
Update(entity T) (rowsAffected int, err error)
|
||||
FetchOne(id Identifier) (entity T, err error)
|
||||
FetchAll() (entities []T, err error)
|
||||
}
|
31
internal/game/ecs/components.go
Normal file
31
internal/game/ecs/components.go
Normal file
|
@ -0,0 +1,31 @@
|
|||
package ecs
|
||||
|
||||
type PlayerState = byte
|
||||
|
||||
const (
|
||||
PlayerStateJoining PlayerState = iota
|
||||
PlayerStateLoggingIn
|
||||
PlayerStateRegistering
|
||||
PlayerStatePlaying
|
||||
PlayerStateLeaving
|
||||
)
|
||||
|
||||
type PlayerStateComponent struct {
|
||||
State PlayerState
|
||||
}
|
||||
|
||||
type NameComponent struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
type DescriptionComponent struct {
|
||||
Description string
|
||||
}
|
||||
|
||||
type InRoomComponent struct {
|
||||
InRoom Entity
|
||||
}
|
||||
|
||||
type NeighboringRoomsComponent struct {
|
||||
North, South, East, West Entity
|
||||
}
|
19
internal/game/ecs/entity.go
Normal file
19
internal/game/ecs/entity.go
Normal file
|
@ -0,0 +1,19 @@
|
|||
package ecs
|
||||
|
||||
import "github.com/google/uuid"
|
||||
|
||||
type Entity uuid.UUID
|
||||
|
||||
type Room struct {
|
||||
Entity
|
||||
NameComponent
|
||||
DescriptionComponent
|
||||
NeighboringRoomsComponent
|
||||
}
|
||||
|
||||
type Player struct {
|
||||
Entity
|
||||
PlayerStateComponent
|
||||
NameComponent
|
||||
InRoomComponent
|
||||
}
|
1
internal/game/ecs/systems.go
Normal file
1
internal/game/ecs/systems.go
Normal file
|
@ -0,0 +1 @@
|
|||
package ecs
|
15
internal/game/ecs/world.go
Normal file
15
internal/game/ecs/world.go
Normal file
|
@ -0,0 +1,15 @@
|
|||
package ecs
|
||||
|
||||
type World struct {
|
||||
Players []*Player
|
||||
Rooms []*Room
|
||||
DefaultRoom *Room
|
||||
}
|
||||
|
||||
func CreateWorld() *World {
|
||||
world := &World{
|
||||
Players: []*Player{},
|
||||
Rooms: []*Room{},
|
||||
}
|
||||
|
||||
}
|
|
@ -45,9 +45,9 @@ type EventBus struct {
|
|||
events chan GameEvent
|
||||
}
|
||||
|
||||
func CreateEventBus() *EventBus {
|
||||
func CreateEventBus(capacity int) *EventBus {
|
||||
return &EventBus{
|
||||
events: make(chan GameEvent, 10),
|
||||
events: make(chan GameEvent, capacity),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -23,8 +23,10 @@ func (pje *PlayerJoinEvent) Type() EventType {
|
|||
}
|
||||
|
||||
func (pje *PlayerJoinEvent) Handle(game *LastMUDGame, delta time.Duration) {
|
||||
game.world.AddPlayerToDefaultRoom(CreatePlayer(pje.connectionId, nil))
|
||||
game.enqeueOutput(game.CreateOutput(pje.connectionId, []byte("Welcome to LastMUD!")))
|
||||
p := CreateJoiningPlayer(pje.connectionId)
|
||||
game.world.AddPlayerToDefaultRoom(p)
|
||||
game.enqeueOutput(game.CreateOutput(p.Identity(), []byte("Welcome to LastMUD!")))
|
||||
game.enqeueOutput(game.CreateOutput(p.Identity(), []byte("Please enter your name:")))
|
||||
}
|
||||
|
||||
type PlayerLeaveEvent struct {
|
||||
|
@ -51,7 +53,7 @@ type PlayerCommandEvent struct {
|
|||
}
|
||||
|
||||
func (game *LastMUDGame) CreatePlayerCommandEvent(connId uuid.UUID, cmdString string) (event *PlayerCommandEvent, err error) {
|
||||
cmdCtx, err := command.CreateCommandContext(game.CommandRegistry(), cmdString)
|
||||
cmdCtx, err := command.CreateCommandContext(game.commandRegistry(), cmdString)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -77,17 +79,23 @@ func (pce *PlayerCommandEvent) Handle(game *LastMUDGame, delta time.Duration) {
|
|||
return
|
||||
}
|
||||
|
||||
event := pce.parseCommandIntoEvent(game, player)
|
||||
}
|
||||
|
||||
func (pce *PlayerCommandEvent) parseCommandIntoEvent(game *LastMUDGame, player *Player) GameEvent {
|
||||
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
|
||||
return nil
|
||||
}
|
||||
|
||||
game.EnqueueEvent(game.CreatePlayerSayEvent(player, speech))
|
||||
return game.CreatePlayerSayEvent(player, speech)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type PlayerSayEvent struct {
|
||||
|
|
|
@ -12,6 +12,9 @@ import (
|
|||
|
||||
const TickRate = time.Duration(50 * time.Millisecond)
|
||||
|
||||
const MaxEnqueuedOutputPerTick = 100
|
||||
const MaxEnqueuedGameEventsPerTick = 100
|
||||
|
||||
type GameOutput struct {
|
||||
connId uuid.UUID
|
||||
contents []byte
|
||||
|
@ -36,8 +39,9 @@ type LastMUDGame struct {
|
|||
ctx context.Context
|
||||
wg *sync.WaitGroup
|
||||
|
||||
commandRegistry *command.CommandRegistry
|
||||
world *World
|
||||
cmdRegistry *command.CommandRegistry
|
||||
|
||||
world *World
|
||||
|
||||
eventBus *EventBus
|
||||
|
||||
|
@ -48,12 +52,12 @@ func CreateGame(ctx context.Context, wg *sync.WaitGroup) (game *LastMUDGame) {
|
|||
game = &LastMUDGame{
|
||||
wg: wg,
|
||||
ctx: ctx,
|
||||
eventBus: CreateEventBus(),
|
||||
output: make(chan GameOutput, 10),
|
||||
eventBus: CreateEventBus(MaxEnqueuedGameEventsPerTick),
|
||||
output: make(chan GameOutput, MaxEnqueuedOutputPerTick),
|
||||
world: CreateWorld(),
|
||||
}
|
||||
|
||||
game.commandRegistry = game.CreateGameCommandRegistry()
|
||||
game.cmdRegistry = game.CreateGameCommandRegistry()
|
||||
|
||||
wg.Add(1)
|
||||
go game.start()
|
||||
|
@ -61,6 +65,23 @@ func CreateGame(ctx context.Context, wg *sync.WaitGroup) (game *LastMUDGame) {
|
|||
return
|
||||
}
|
||||
|
||||
func (game *LastMUDGame) EnqueueEvent(event GameEvent) {
|
||||
game.eventBus.Push(event)
|
||||
}
|
||||
|
||||
func (game *LastMUDGame) ConsumeNextOutput() *GameOutput {
|
||||
select {
|
||||
case output := <-game.output:
|
||||
return &output
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (game *LastMUDGame) commandRegistry() *command.CommandRegistry {
|
||||
return game.cmdRegistry
|
||||
}
|
||||
|
||||
func (game *LastMUDGame) start() {
|
||||
defer game.wg.Done()
|
||||
defer game.shutdown()
|
||||
|
@ -102,27 +123,10 @@ func (game *LastMUDGame) shouldStop() bool {
|
|||
}
|
||||
}
|
||||
|
||||
func (game *LastMUDGame) EnqueueEvent(event GameEvent) {
|
||||
game.eventBus.Push(event)
|
||||
}
|
||||
|
||||
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 (game *LastMUDGame) CommandRegistry() *command.CommandRegistry {
|
||||
return game.commandRegistry
|
||||
}
|
||||
|
||||
func (g *LastMUDGame) tick(delta time.Duration) {
|
||||
for {
|
||||
event := g.eventBus.Pop()
|
||||
|
|
|
@ -5,12 +5,23 @@ import "github.com/google/uuid"
|
|||
type Player struct {
|
||||
id uuid.UUID
|
||||
|
||||
state PlayerState
|
||||
|
||||
currentRoom *Room
|
||||
}
|
||||
|
||||
func CreatePlayer(identity uuid.UUID, room *Room) *Player {
|
||||
func CreateJoiningPlayer(identity uuid.UUID) *Player {
|
||||
return &Player{
|
||||
id: identity,
|
||||
state: PlayerStateJoining,
|
||||
currentRoom: nil,
|
||||
}
|
||||
}
|
||||
|
||||
func CreatePlayer(identity uuid.UUID, state PlayerState, room *Room) *Player {
|
||||
return &Player{
|
||||
id: identity,
|
||||
state: state,
|
||||
currentRoom: room,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
package game
|
|
@ -11,7 +11,11 @@ import (
|
|||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
const MaxLastSeenTime = 120 * time.Second
|
||||
const MaxLastSeenTime = 90 * time.Second
|
||||
|
||||
const CheckAlivePeriod = 50 * time.Millisecond
|
||||
|
||||
const DeleteBeforeAndMoveToStartOfLine = "\033[1K\r"
|
||||
|
||||
type Connection struct {
|
||||
ctx context.Context
|
||||
|
@ -53,6 +57,13 @@ func (c *Connection) Id() uuid.UUID {
|
|||
return c.identity
|
||||
}
|
||||
|
||||
func (c *Connection) Write(output []byte) (err error) {
|
||||
output = append([]byte(DeleteBeforeAndMoveToStartOfLine+"< "), output...)
|
||||
output = append(output, []byte("\n> ")...)
|
||||
_, err = c.conn.Write(output)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Connection) listen() {
|
||||
defer c.wg.Done()
|
||||
|
||||
|
@ -100,6 +111,8 @@ func (c *Connection) checkAlive() {
|
|||
if err != nil {
|
||||
break
|
||||
}
|
||||
|
||||
time.Sleep(CheckAlivePeriod)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -119,10 +132,3 @@ func (c *Connection) closeConnection() {
|
|||
|
||||
logging.Info("Disconnected: ", c.conn.RemoteAddr())
|
||||
}
|
||||
|
||||
func (c *Connection) Write(output []byte) (err error) {
|
||||
output = append([]byte("< "), output...)
|
||||
output = append(output, []byte("\n> ")...)
|
||||
_, err = c.conn.Write(output)
|
||||
return
|
||||
}
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
package server
|
||||
|
||||
type inputEmptyError struct {
|
||||
msg string
|
||||
}
|
||||
|
||||
func newInputEmptyError() *inputEmptyError {
|
||||
return &inputEmptyError{
|
||||
msg: "No input available at this moment",
|
||||
}
|
||||
}
|
||||
|
||||
func (err *inputEmptyError) Error() string {
|
||||
return err.msg
|
||||
}
|
Loading…
Add table
Reference in a new issue