hermeticum/server/cmd/main.go

328 lines
7.6 KiB
Go
Raw Normal View History

2022-07-07 22:07:03 +00:00
package main
import (
"context"
"flag"
"fmt"
"log"
"net"
2022-07-22 21:57:15 +00:00
"sync"
2022-07-07 22:07:03 +00:00
"github.com/vilmibm/hermeticum/proto"
"github.com/vilmibm/hermeticum/server/db"
2022-12-20 08:38:47 +00:00
"github.com/vilmibm/hermeticum/server/witch"
2022-07-07 22:07:03 +00:00
"google.golang.org/grpc"
)
var (
tls = flag.Bool("tls", false, "Connection uses TLS if true, else plain TCP")
certFile = flag.String("cert_file", "", "The TLS cert file")
keyFile = flag.String("key_file", "", "The TLS key file")
port = flag.Int("port", 6666, "The server port")
)
func _main() (err error) {
l, err := net.Listen("tcp", fmt.Sprintf(":%d", *port))
if err != nil {
return err
}
var opts []grpc.ServerOption
if *tls {
log.Fatal("tls unsupported")
/*
// TODO base some stuff on the data package in the examples to get tls working
if *certFile == "" {
*certFile = data.Path("x509/server_cert.pem")
}
if *keyFile == "" {
*keyFile = data.Path("x509/server_key.pem")
}
creds, err := credentials.NewServerTLSFromFile(*certFile, *keyFile)
if err != nil {
log.Fatalf("Failed to generate credentials %v", err)
}
opts = []grpc.ServerOption{grpc.Creds(creds)}
*/
}
grpcServer := grpc.NewServer(opts...)
2022-07-28 01:45:21 +00:00
srv, err := newServer()
if err != nil {
return err
}
proto.RegisterGameWorldServer(grpcServer, srv)
2022-07-07 22:07:03 +00:00
grpcServer.Serve(l)
return nil
}
type gameWorldServer struct {
proto.UnimplementedGameWorldServer
2022-07-22 21:57:15 +00:00
2022-12-24 05:31:08 +00:00
db db.DB
msgRouterMutex sync.Mutex
msgRouter map[string]func(*proto.ClientMessage) error
scripts map[int]*witch.ScriptContext
scriptsMutex sync.RWMutex
2022-07-07 22:07:03 +00:00
}
2022-07-28 01:45:21 +00:00
func newServer() (*gameWorldServer, error) {
// TODO read from env or whatever
db, err := db.NewDB("postgres://vilmibm:vilmibm@localhost:5432/hermeticum")
if err != nil {
return nil, err
}
2023-01-08 08:22:04 +00:00
reset := flag.Bool("reset", false, "fully reset the database to its initial state")
flag.Parse()
if *reset {
if err = db.Erase(); err != nil {
return nil, fmt.Errorf("failed to reset database: %w", err)
}
}
if err = db.Ensure(); err != nil {
return nil, fmt.Errorf("failed to ensure default entities: %w", err)
}
2022-12-14 07:52:29 +00:00
if err = db.ClearSessions(); err != nil {
return nil, fmt.Errorf("could not clear sessions: %w", err)
}
2022-07-22 21:57:15 +00:00
s := &gameWorldServer{
2022-12-24 05:31:08 +00:00
msgRouter: make(map[string]func(*proto.ClientMessage) error),
db: db,
scripts: make(map[int]*witch.ScriptContext),
scriptsMutex: sync.RWMutex{},
2022-07-22 21:57:15 +00:00
}
2022-07-28 01:45:21 +00:00
return s, nil
2022-07-07 22:07:03 +00:00
}
2022-12-24 05:31:08 +00:00
func (s *gameWorldServer) verbHandler(verb, rest string, sender, target db.Object) error {
log.Printf("VH %s %s %d %d", verb, rest, sender.ID, target.ID)
2022-12-24 05:31:08 +00:00
s.scriptsMutex.RLock()
sc, ok := s.scripts[target.ID]
s.scriptsMutex.RUnlock()
2022-12-24 06:34:21 +00:00
var err error
2022-12-24 05:31:08 +00:00
2023-04-04 06:41:05 +00:00
getSend := func(sid string) func(*proto.ClientMessage) error {
return s.msgRouter[sid]
2022-12-28 05:19:42 +00:00
}
2022-12-30 04:01:12 +00:00
if !ok || sc == nil {
2023-04-04 06:41:05 +00:00
if sc, err = witch.NewScriptContext(s.db, getSend); err != nil {
2022-12-24 05:31:08 +00:00
return err
}
s.scriptsMutex.Lock()
s.scripts[target.ID] = sc
s.scriptsMutex.Unlock()
}
2022-12-24 06:34:21 +00:00
vc := witch.VerbContext{
Verb: verb,
Rest: rest,
Sender: sender,
Target: target,
}
sc.Handle(vc)
return nil
2022-12-24 05:31:08 +00:00
}
func (s *gameWorldServer) HandleCmd(verb, rest string, sender *db.Object) {
// TODO
}
2022-12-30 04:42:54 +00:00
func (s *gameWorldServer) endSession(sid string) {
var err error
var avatar *db.Object
2022-12-30 04:01:12 +00:00
log.Printf("ending session %s", sid)
2022-12-30 04:42:54 +00:00
avatar, err = s.db.AvatarBySessionID(sid)
2022-12-30 04:01:12 +00:00
if err != nil {
2022-12-30 04:42:54 +00:00
log.Printf("error while ending session %s: %s", sid, err.Error())
2022-12-30 04:01:12 +00:00
} else {
s.scriptsMutex.Lock()
2022-12-30 04:42:54 +00:00
delete(s.scripts, avatar.ID)
2022-12-30 04:01:12 +00:00
s.scriptsMutex.Unlock()
}
2022-12-30 04:42:54 +00:00
if err = s.db.EndSession(sid); err != nil {
log.Printf("error while ending session %s: %s", sid, err.Error())
2022-12-30 04:01:12 +00:00
}
2022-12-30 04:42:54 +00:00
delete(s.msgRouter, sid)
2022-12-30 04:01:12 +00:00
}
2022-07-22 21:10:12 +00:00
func (s *gameWorldServer) Commands(stream proto.GameWorld_CommandsServer) error {
2022-07-28 01:45:21 +00:00
var sid string
2022-12-30 04:01:12 +00:00
var cmd *proto.Command
var err error
var avatar *db.Object
var send func(*proto.ClientMessage) error
var affected []db.Object
var o db.Object
2022-07-22 21:10:12 +00:00
for {
2022-12-30 04:01:12 +00:00
if cmd, err = stream.Recv(); err != nil {
log.Printf("commands stream closed with error: %s", err.Error())
return err
}
if sid == "" {
sid = cmd.SessionInfo.SessionID
defer s.endSession(sid)
2022-07-22 21:10:12 +00:00
}
2022-12-30 04:01:12 +00:00
2022-12-30 04:42:54 +00:00
if err = stream.Send(&proto.CommandAck{
2022-12-30 04:01:12 +00:00
Acked: true,
2022-12-30 04:42:54 +00:00
}); err != nil {
2022-12-30 04:01:12 +00:00
log.Printf("unable to ack command in session %s", sid)
2022-07-22 21:10:12 +00:00
return err
}
2022-12-30 04:01:12 +00:00
if send == nil {
2022-12-30 04:42:54 +00:00
log.Printf("saving a send fn for session %s", sid)
2022-12-30 04:01:12 +00:00
send = s.msgRouter[sid]
}
2022-07-28 02:05:48 +00:00
2022-12-30 04:01:12 +00:00
if avatar, err = s.db.AvatarBySessionID(sid); err != nil {
return s.HandleError(send, err)
}
log.Printf("verb %s from avatar %d in session %s", cmd.Verb, avatar.ID, sid)
2022-07-28 02:05:48 +00:00
if cmd.Verb == "quit" || cmd.Verb == "q" {
2022-12-30 04:01:12 +00:00
return nil
2022-07-28 02:05:48 +00:00
}
2022-07-22 21:57:15 +00:00
2022-12-30 04:01:12 +00:00
if affected, err = s.db.Earshot(*avatar); err != nil {
2022-12-14 07:52:29 +00:00
return s.HandleError(send, err)
2022-07-22 21:57:15 +00:00
}
2022-12-23 06:55:35 +00:00
2023-01-11 03:05:43 +00:00
for _, obj := range affected {
log.Printf("%s heard %s from %d", obj.Data["name"], cmd.Verb, avatar.ID)
}
2022-12-30 04:01:12 +00:00
for _, o = range affected {
if err = s.verbHandler(cmd.Verb, cmd.Rest, *avatar, o); err != nil {
log.Printf("error handling verb %s for object %d: %s", cmd.Verb, o.ID, err)
}
2022-12-23 06:55:35 +00:00
}
2022-12-24 06:34:21 +00:00
//s.HandleCmd(cmd.Verb, cmd.Rest, avatar)
2022-07-22 21:10:12 +00:00
}
}
2022-07-07 22:07:03 +00:00
func (s *gameWorldServer) Ping(ctx context.Context, _ *proto.SessionInfo) (*proto.Pong, error) {
pong := &proto.Pong{
When: "TODO",
}
return pong, nil
}
2022-07-09 07:15:24 +00:00
func (s *gameWorldServer) Messages(si *proto.SessionInfo, stream proto.GameWorld_MessagesServer) error {
2022-12-24 05:31:08 +00:00
s.msgRouterMutex.Lock()
2022-07-22 21:57:15 +00:00
s.msgRouter[si.SessionID] = stream.Send
2022-12-24 05:31:08 +00:00
s.msgRouterMutex.Unlock()
2022-07-22 21:57:15 +00:00
// TODO this is clearly bad but it works. I should refactor this so that messages are received on a channel.
for {
2022-07-09 07:15:24 +00:00
}
}
func (s *gameWorldServer) Register(ctx context.Context, auth *proto.AuthInfo) (si *proto.SessionInfo, err error) {
2022-07-28 03:30:23 +00:00
var account *db.Account
account, err = s.db.CreateAccount(auth.Username, auth.Password)
if err != nil {
2022-07-28 03:30:23 +00:00
return
}
2022-08-01 15:15:18 +00:00
var sessionID string
sessionID, err = s.db.StartSession(*account)
2022-07-28 03:30:23 +00:00
if err != nil {
return nil, fmt.Errorf("failed to start session for %d: %w", account.ID, err)
}
2022-08-01 15:15:18 +00:00
log.Printf("started session for %s", account.Name)
2022-08-01 15:15:18 +00:00
av, err := s.db.AvatarBySessionID(sessionID)
if err != nil {
return nil, fmt.Errorf("failed to find avatar for %s: %w", sessionID, err)
2022-08-01 15:15:18 +00:00
}
2022-07-28 03:30:23 +00:00
foyer, err := s.db.GetObject("system", "foyer")
2022-08-01 15:15:18 +00:00
if err != nil {
return nil, fmt.Errorf("failed to find foyer: %w", err)
2022-08-01 15:15:18 +00:00
}
2022-07-28 03:30:23 +00:00
if err = s.db.MoveInto(*av, *foyer); err != nil {
return nil, fmt.Errorf("failed to move %d into %d: %w", av.ID, foyer.ID, err)
}
2022-08-01 15:15:18 +00:00
// TODO send room info, avatar info to client (need to figure this out and update proto)
si = &proto.SessionInfo{SessionID: sessionID}
return
}
func (s *gameWorldServer) Login(ctx context.Context, auth *proto.AuthInfo) (si *proto.SessionInfo, err error) {
var a *db.Account
2022-07-28 01:45:21 +00:00
a, err = s.db.ValidateCredentials(auth.Username, auth.Password)
if err != nil {
return
}
var sessionID string
2022-07-28 01:45:21 +00:00
sessionID, err = s.db.StartSession(*a)
if err != nil {
return
}
2022-12-14 07:52:29 +00:00
av, err := s.db.AvatarBySessionID(sessionID)
if err != nil {
return nil, fmt.Errorf("failed to find avatar for %s: %w", sessionID, err)
}
2022-12-30 04:42:54 +00:00
foyer, err := s.db.GetObject("system", "foyer")
2022-12-14 07:52:29 +00:00
if err != nil {
2022-12-30 04:42:54 +00:00
return nil, fmt.Errorf("failed to find foyer: %w", err)
2022-12-14 07:52:29 +00:00
}
2022-12-30 04:42:54 +00:00
if err = s.db.MoveInto(*av, *foyer); err != nil {
return nil, fmt.Errorf("failed to move %d into %d: %w", av.ID, foyer.ID, err)
2022-12-14 07:52:29 +00:00
}
si = &proto.SessionInfo{SessionID: sessionID}
return
2022-07-07 22:07:03 +00:00
}
2022-12-14 07:52:29 +00:00
func (s *gameWorldServer) HandleError(send func(*proto.ClientMessage) error, err error) error {
log.Printf("error: %s", err.Error())
msg := &proto.ClientMessage{
Type: proto.ClientMessage_WHISPER,
Text: "server error :(",
}
err = send(msg)
if err != nil {
log.Printf("error sending to client: %s", err.Error())
}
return err
}
2022-07-07 22:07:03 +00:00
// TODO other server functions
func main() {
err := _main()
if err != nil {
2022-07-07 22:12:22 +00:00
log.Fatal(err.Error())
2022-07-07 22:07:03 +00:00
}
}