Compare commits
10 Commits
c069782f32
...
3bdc35e197
| Author | SHA1 | Date |
|---|---|---|
|
|
3bdc35e197 | 1 week ago |
|
|
77123d56b2 | 1 week ago |
|
|
aa4d962a60 | 1 week ago |
|
|
08e4039518 | 1 week ago |
|
|
a75e6d7eec | 1 week ago |
|
|
3ed07a7887 | 1 week ago |
|
|
d22b4a893c | 1 week ago |
|
|
7a4d66bcf8 | 1 week ago |
|
|
b4e7c1c0a8 | 2 weeks ago |
|
|
9781fb6736 | 2 weeks ago |
@ -0,0 +1,69 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
_ "embed"
|
||||
"log"
|
||||
|
||||
_ "modernc.org/sqlite"
|
||||
|
||||
models "code.ndumas.com/ndumas/muddy/db"
|
||||
)
|
||||
|
||||
//go:embed schema.sql
|
||||
var ddl string
|
||||
|
||||
func run() error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
ctx := context.Background()
|
||||
|
||||
db, err := sql.Open("sqlite", ":memory:")
|
||||
if err != nil {
|
||||
log.Printf("error opening sqlite db: %s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
// create tables
|
||||
if _, err := db.ExecContext(ctx, ddl); err != nil {
|
||||
log.Printf("error creating tables: %s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
queries := models.New(db)
|
||||
|
||||
obsvID, err := queries.CreateObservable(
|
||||
ctx,
|
||||
models.CreateObservableParams{
|
||||
Name: "The Crossroads",
|
||||
Description: "You stand at the intersection of two well trod roads. You don't remember how you got here, where you were going, or who you were. All you know is that you must go forward.",
|
||||
},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
log.Printf("error creating room observable: %s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Printf("error creating room observable: %s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
locID, err := queries.CreateLocation(
|
||||
ctx,
|
||||
models.CreateLocationParams{
|
||||
X: 0,
|
||||
Y: 0,
|
||||
Z: 0,
|
||||
World: 0,
|
||||
Observable: obsvID,
|
||||
},
|
||||
)
|
||||
|
||||
log.Printf("created room: %d\n", locID)
|
||||
}
|
||||
@ -0,0 +1,32 @@
|
||||
CREATE TABLE locations (
|
||||
id INTEGER PRIMARY KEY,
|
||||
x INTEGER NOT NULL,
|
||||
y INTEGER NOT NULL,
|
||||
z INTEGER NOT NULL,
|
||||
world INTEGER NOT NULL,
|
||||
observable INTEGER NOT NULL references observables(id)
|
||||
);
|
||||
|
||||
|
||||
CREATE TABLE observables (
|
||||
id INTEGER PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
description TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE inventories (
|
||||
id INTEGER PRIMARY KEY
|
||||
);
|
||||
|
||||
CREATE TABLE pools (
|
||||
id INTEGER PRIMARY KEY,
|
||||
type INTEGER NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE exits (
|
||||
id INTEGER PRIMARY KEY,
|
||||
origin INTEGER NOT NULL,
|
||||
dest INTEGER NOT NULL,
|
||||
FOREIGN KEY (origin) REFERENCES locations (id),
|
||||
FOREIGN KEY (dest) REFERENCES locations (id)
|
||||
);
|
||||
@ -0,0 +1,57 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"io"
|
||||
"log/slog"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"code.ndumas.com/ndumas/muddy/core"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var (
|
||||
level string
|
||||
logFile string
|
||||
)
|
||||
|
||||
flag.StringVar(&level, "level", "INFO", "Log level: INFO/info, DEBUG/debug, or ERROR/error.")
|
||||
flag.StringVar(&logFile, "log", "out.log", "Path to log file.")
|
||||
|
||||
flag.Parse()
|
||||
|
||||
f, err := os.Create(logFile)
|
||||
if err != nil {
|
||||
slog.With(
|
||||
slog.Any("error", err),
|
||||
).Error("could not open log file")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
appConfig := core.AppConfig{
|
||||
Port: ":3001",
|
||||
|
||||
LogDest: io.MultiWriter(f, os.Stderr),
|
||||
LogLevel: level,
|
||||
|
||||
TickInterval: time.Millisecond * 30,
|
||||
}
|
||||
|
||||
app := core.New(appConfig)
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
c := make(chan os.Signal, 1)
|
||||
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
|
||||
go func() {
|
||||
<-c
|
||||
app.L.Info("shutting down")
|
||||
cancel()
|
||||
}()
|
||||
|
||||
app.Run(ctx)
|
||||
}
|
||||
@ -0,0 +1,68 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log/slog"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/EngoEngine/ecs"
|
||||
)
|
||||
|
||||
type Server struct {
|
||||
l *slog.Logger
|
||||
port string
|
||||
ctx context.Context
|
||||
Sessions chan *Session
|
||||
ln net.Listener
|
||||
w *ecs.World
|
||||
}
|
||||
|
||||
func NewServer(ctx context.Context, l *slog.Logger, port string, w *ecs.World) *Server {
|
||||
var s Server
|
||||
|
||||
c := make(chan *Session)
|
||||
s.Sessions = c
|
||||
lc := net.ListenConfig{
|
||||
KeepAlive: 5 * time.Minute,
|
||||
KeepAliveConfig: net.KeepAliveConfig{
|
||||
Enable: true,
|
||||
Idle: 3 * time.Minute,
|
||||
Interval: 15 * time.Second,
|
||||
},
|
||||
}
|
||||
|
||||
ln, err := lc.Listen(ctx, "tcp", port)
|
||||
if err != nil {
|
||||
s.l.With(
|
||||
slog.Any("error", err),
|
||||
).Error("could not start tcp listener")
|
||||
return &s
|
||||
}
|
||||
|
||||
s.ctx = ctx
|
||||
s.ln = ln
|
||||
s.l = l
|
||||
s.port = port
|
||||
s.w = w
|
||||
|
||||
return &s
|
||||
}
|
||||
|
||||
func (s *Server) Start() {
|
||||
s.l.Debug("starting telnet server")
|
||||
for {
|
||||
select {
|
||||
case <-s.ctx.Done():
|
||||
return
|
||||
default:
|
||||
conn, err := s.ln.Accept()
|
||||
if err != nil {
|
||||
s.l.With(
|
||||
slog.Any("error", err),
|
||||
).Error("could not accept tcp connection")
|
||||
}
|
||||
s.Sessions <- NewSession(s.ctx, conn, Printer{}, s.l)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,75 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"net"
|
||||
|
||||
"github.com/EngoEngine/ecs"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type Session struct {
|
||||
c net.Conn
|
||||
ctx context.Context
|
||||
p Printer
|
||||
l *slog.Logger
|
||||
|
||||
w *ecs.World
|
||||
|
||||
sessionID uuid.UUID
|
||||
userID uint64
|
||||
}
|
||||
|
||||
func NewSession(ctx context.Context, c net.Conn, p Printer, l *slog.Logger, w *ecs.World) *Session {
|
||||
sid := uuid.New()
|
||||
return &Session{
|
||||
ctx: ctx,
|
||||
|
||||
l: l.With(slog.String("sid", sid.String())),
|
||||
c: c,
|
||||
w: w,
|
||||
p: p,
|
||||
|
||||
sessionID: sid,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Session) bufferInput(commands chan string) {
|
||||
scanner := bufio.NewScanner(s.c)
|
||||
defer close(commands)
|
||||
|
||||
for scanner.Scan() {
|
||||
err := scanner.Err()
|
||||
switch err {
|
||||
case nil:
|
||||
commands <- scanner.Text()
|
||||
default:
|
||||
s.l.With(slog.Any("error", scanner.Err())).Error("error scanning input")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
s.l.Debug("session terminated")
|
||||
}
|
||||
|
||||
func (s *Session) Start() {
|
||||
s.l.Debug("starting telnet session")
|
||||
fmt.Fprint(s.c, "Welcome to Bing Bong.\n")
|
||||
|
||||
commands := make(chan string)
|
||||
go s.bufferInput(commands)
|
||||
|
||||
for command := range commands {
|
||||
s.l.With(slog.String("command", command)).Debug("command recieved")
|
||||
_, err := fmt.Fprintf(s.c, "> %q\n", command)
|
||||
if err != nil {
|
||||
s.l.With(
|
||||
slog.Any("error", err),
|
||||
).Error("connection closed on write")
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,56 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.30.0
|
||||
|
||||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
type Querier interface {
|
||||
// Location
|
||||
//
|
||||
// insert INTO locations (x,y,z,world, observable) VALUES (?,?,?,?,?)
|
||||
CreateLocation(ctx context.Context, arg CreateLocationParams) (int64, error)
|
||||
// Observable
|
||||
//
|
||||
// insert INTO observables (name, description) VALUES (?,?)
|
||||
CreateObservable(ctx context.Context, arg CreateObservableParams) (int64, error)
|
||||
//DestroyLocation
|
||||
//
|
||||
// delete from locations where id = ?
|
||||
DestroyLocation(ctx context.Context, id int64) error
|
||||
// Exit
|
||||
//
|
||||
// select id, origin, dest from exits where id = ? LIMIT 1
|
||||
GetExit(ctx context.Context, id int64) (Exit, error)
|
||||
//GetLocation
|
||||
//
|
||||
// select id, x, y, z, world, observable from locations where id = ? LIMIT 1
|
||||
GetLocation(ctx context.Context, id int64) (Location, error)
|
||||
//GetLocationExitsFrom
|
||||
//
|
||||
// select id, origin, dest from exits where dest = ?
|
||||
GetLocationExitsFrom(ctx context.Context, dest int64) ([]Exit, error)
|
||||
//GetLocationExitsTo
|
||||
//
|
||||
// select id, origin, dest from exits where origin = ?
|
||||
GetLocationExitsTo(ctx context.Context, origin int64) ([]Exit, error)
|
||||
//GetObservable
|
||||
//
|
||||
// select id, name, description from observables where id = ? LIMIT 1
|
||||
GetObservable(ctx context.Context, id int64) (Observable, error)
|
||||
//UpdateLocation
|
||||
//
|
||||
// update locations SET
|
||||
// x = ?,
|
||||
// y = ?,
|
||||
// z = ?,
|
||||
// world = ?,
|
||||
// observable = ?
|
||||
// where id = ?
|
||||
UpdateLocation(ctx context.Context, arg UpdateLocationParams) error
|
||||
}
|
||||
|
||||
var _ Querier = (*Queries)(nil)
|
||||
@ -1,11 +1,30 @@
|
||||
-- name: CreateLocation :exec
|
||||
insert INTO LOCATIONS (x,y,z,world) VALUES (?,?,?,?);
|
||||
|
||||
-- Location
|
||||
-- name: CreateLocation :execlastid
|
||||
insert INTO locations (x,y,z,world, observable) VALUES (?,?,?,?,?);
|
||||
-- name: GetLocation :one
|
||||
select * from locations where id = ? LIMIT 1;
|
||||
-- name: UpdateLocation :exec
|
||||
update locations SET
|
||||
x = ?,
|
||||
y = ?,
|
||||
z = ?,
|
||||
world = ?,
|
||||
observable = ?
|
||||
where id = ?;
|
||||
-- name: DestroyLocation :exec
|
||||
delete from locations where id = ?;
|
||||
|
||||
-- name: GetLocationExitsTo :many
|
||||
select * from exits where to = ? LIMIT 1;
|
||||
|
||||
-- Exit
|
||||
-- name: GetExit :one
|
||||
select * from exits where id = ? LIMIT 1;
|
||||
-- name: GetLocationExitsTo :many
|
||||
select * from exits where origin = ?;
|
||||
-- name: GetLocationExitsFrom :many
|
||||
select * from exits where from = ? LIMIT 1;
|
||||
select * from exits where dest = ?;
|
||||
|
||||
-- Observable
|
||||
-- name: CreateObservable :execlastid
|
||||
insert INTO observables (name, description) VALUES (?,?);
|
||||
-- name: GetObservable :one
|
||||
select * from observables where id = ? LIMIT 1;
|
||||
|
||||
@ -1,28 +1,32 @@
|
||||
create TABLE locations (
|
||||
CREATE TABLE locations (
|
||||
id INTEGER PRIMARY KEY,
|
||||
x INTEGER NOT NULL,
|
||||
y INTEGER NOT NULL,
|
||||
z INTEGER NOT NULL,
|
||||
world INTEGER NOT NULL
|
||||
)
|
||||
world INTEGER NOT NULL,
|
||||
observable INTEGER NOT NULL references observables(id)
|
||||
);
|
||||
|
||||
create TABLE exits (
|
||||
id INTEGER PRIMARY KEY,
|
||||
to INTEGER REFERENCES locations(id),
|
||||
from INTEGER REFERENCES locations(id)
|
||||
)
|
||||
|
||||
create TABLE observables (
|
||||
CREATE TABLE observables (
|
||||
id INTEGER PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
description TEXT NOT NULL
|
||||
)
|
||||
);
|
||||
|
||||
create TABLE inventories (
|
||||
CREATE TABLE inventories (
|
||||
id INTEGER PRIMARY KEY
|
||||
)
|
||||
);
|
||||
|
||||
create TABLE pools (
|
||||
CREATE TABLE pools (
|
||||
id INTEGER PRIMARY KEY,
|
||||
type INTEGER NOT NULL
|
||||
)
|
||||
);
|
||||
|
||||
CREATE TABLE exits (
|
||||
id INTEGER PRIMARY KEY,
|
||||
origin INTEGER NOT NULL,
|
||||
dest INTEGER NOT NULL,
|
||||
FOREIGN KEY (origin) REFERENCES locations (id),
|
||||
FOREIGN KEY (dest) REFERENCES locations (id)
|
||||
);
|
||||
|
||||
Loading…
Reference in New Issue