WIP very sleepy
This commit is contained in:
		
							parent
							
								
									76382a2b36
								
							
						
					
					
						commit
						7413b12b4a
					
				@ -12,6 +12,7 @@ import (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	"github.com/vilmibm/hermeticum/proto"
 | 
						"github.com/vilmibm/hermeticum/proto"
 | 
				
			||||||
	"github.com/vilmibm/hermeticum/server/db"
 | 
						"github.com/vilmibm/hermeticum/server/db"
 | 
				
			||||||
 | 
						"github.com/vilmibm/hermeticum/server/witch"
 | 
				
			||||||
	"google.golang.org/grpc"
 | 
						"google.golang.org/grpc"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -73,6 +74,7 @@ type gameWorldServer struct {
 | 
				
			|||||||
	db        db.DB
 | 
						db        db.DB
 | 
				
			||||||
	mu        sync.Mutex // for msgRouter
 | 
						mu        sync.Mutex // for msgRouter
 | 
				
			||||||
	msgRouter map[string]func(*proto.ClientMessage) error
 | 
						msgRouter map[string]func(*proto.ClientMessage) error
 | 
				
			||||||
 | 
						Gateway   *witch.Gateway
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func newServer() (*gameWorldServer, error) {
 | 
					func newServer() (*gameWorldServer, error) {
 | 
				
			||||||
@ -89,6 +91,7 @@ func newServer() (*gameWorldServer, error) {
 | 
				
			|||||||
	s := &gameWorldServer{
 | 
						s := &gameWorldServer{
 | 
				
			||||||
		msgRouter: make(map[string]func(*proto.ClientMessage) error),
 | 
							msgRouter: make(map[string]func(*proto.ClientMessage) error),
 | 
				
			||||||
		db:        db,
 | 
							db:        db,
 | 
				
			||||||
 | 
							Gateway:   witch.NewGateway(),
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return s, nil
 | 
						return s, nil
 | 
				
			||||||
@ -251,20 +254,20 @@ func (s *gameWorldServer) Login(ctx context.Context, auth *proto.AuthInfo) (si *
 | 
				
			|||||||
	return
 | 
						return
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (s *gameWorldServer) HandleSay(avatar *db.Object, msg string) error {
 | 
					func (s *gameWorldServer) HandleSay(sender *db.Object, msg string) error {
 | 
				
			||||||
	name := avatar.Data["name"]
 | 
						name := sender.Data["name"]
 | 
				
			||||||
	if name == "" {
 | 
						if name == "" {
 | 
				
			||||||
		// TODO determine this based on a hash or something
 | 
							// TODO determine this based on a hash or something
 | 
				
			||||||
		name = "a mysterious figure"
 | 
							name = "a mysterious figure"
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	heard, err := s.db.Earshot(*avatar)
 | 
						heard, err := s.db.Earshot(*sender)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		log.Println(err.Error())
 | 
							log.Println(err.Error())
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	log.Printf("found %#v in earshot of %#v\n", heard, avatar)
 | 
						log.Printf("found %#v in earshot of %#v\n", heard, sender)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	as, err := s.db.ActiveSessions()
 | 
						as, err := s.db.ActiveSessions()
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
@ -274,6 +277,7 @@ func (s *gameWorldServer) HandleSay(avatar *db.Object, msg string) error {
 | 
				
			|||||||
	sendErrs := []error{}
 | 
						sendErrs := []error{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for _, h := range heard {
 | 
						for _, h := range heard {
 | 
				
			||||||
 | 
							s.Gateway.VerbHandler(msg, *sender, h)
 | 
				
			||||||
		// TODO once we have a script engine, deliver the HEARS event
 | 
							// TODO once we have a script engine, deliver the HEARS event
 | 
				
			||||||
		for _, sess := range as {
 | 
							for _, sess := range as {
 | 
				
			||||||
			if sess.AccountID == h.OwnerID {
 | 
								if sess.AccountID == h.OwnerID {
 | 
				
			||||||
 | 
				
			|||||||
@ -51,6 +51,7 @@ type Object struct {
 | 
				
			|||||||
	Bedroom bool
 | 
						Bedroom bool
 | 
				
			||||||
	Data    map[string]string
 | 
						Data    map[string]string
 | 
				
			||||||
	OwnerID int
 | 
						OwnerID int
 | 
				
			||||||
 | 
						Script  string
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type pgDB struct {
 | 
					type pgDB struct {
 | 
				
			||||||
@ -203,11 +204,11 @@ func (db *pgDB) AvatarBySessionID(sid string) (avatar *Object, err error) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	// TODO subquery
 | 
						// TODO subquery
 | 
				
			||||||
	stmt := `
 | 
						stmt := `
 | 
				
			||||||
	SELECT id, avatar, data, owner
 | 
						SELECT id, avatar, data, owner, script
 | 
				
			||||||
	FROM objects WHERE avatar = true AND owner = (
 | 
						FROM objects WHERE avatar = true AND owner = (
 | 
				
			||||||
		SELECT a.id FROM sessions s JOIN accounts a ON s.account = a.id WHERE s.id = $1)`
 | 
							SELECT a.id FROM sessions s JOIN accounts a ON s.account = a.id WHERE s.id = $1)`
 | 
				
			||||||
	err = db.pool.QueryRow(context.Background(), stmt, sid).Scan(
 | 
						err = db.pool.QueryRow(context.Background(), stmt, sid).Scan(
 | 
				
			||||||
		&avatar.ID, &avatar.Avatar, &avatar.Data, &avatar.OwnerID)
 | 
							&avatar.ID, &avatar.Avatar, &avatar.Data, &avatar.OwnerID, &avatar.Script)
 | 
				
			||||||
	return
 | 
						return
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -249,7 +250,7 @@ func (db *pgDB) MoveInto(toMove Object, container Object) error {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func (db *pgDB) Earshot(obj Object) ([]Object, error) {
 | 
					func (db *pgDB) Earshot(obj Object) ([]Object, error) {
 | 
				
			||||||
	stmt := `
 | 
						stmt := `
 | 
				
			||||||
	SELECT id, avatar, bedroom, data, owner FROM objects
 | 
						SELECT id, avatar, bedroom, data, owner, script FROM objects
 | 
				
			||||||
	WHERE id IN (
 | 
						WHERE id IN (
 | 
				
			||||||
		SELECT contained FROM contains
 | 
							SELECT contained FROM contains
 | 
				
			||||||
		WHERE container = (
 | 
							WHERE container = (
 | 
				
			||||||
@ -263,7 +264,10 @@ func (db *pgDB) Earshot(obj Object) ([]Object, error) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	for rows.Next() {
 | 
						for rows.Next() {
 | 
				
			||||||
		heard := Object{}
 | 
							heard := Object{}
 | 
				
			||||||
		if err = rows.Scan(&heard.ID, &heard.Avatar, &heard.Bedroom, &heard.Data, &heard.OwnerID); err != nil {
 | 
							if err = rows.Scan(
 | 
				
			||||||
 | 
								&heard.ID, &heard.Avatar,
 | 
				
			||||||
 | 
								&heard.Bedroom, &heard.Data,
 | 
				
			||||||
 | 
								&heard.OwnerID, &heard.Script); err != nil {
 | 
				
			||||||
			return nil, err
 | 
								return nil, err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		out = append(out, heard)
 | 
							out = append(out, heard)
 | 
				
			||||||
 | 
				
			|||||||
@ -17,7 +17,7 @@ CREATE TABLE objects (
 | 
				
			|||||||
  avatar    boolean NOT NULL DEFAULT FALSE,
 | 
					  avatar    boolean NOT NULL DEFAULT FALSE,
 | 
				
			||||||
  bedroom   boolean NOT NULL DEFAULT FALSE,
 | 
					  bedroom   boolean NOT NULL DEFAULT FALSE,
 | 
				
			||||||
  data      jsonb   NOT NULL,
 | 
					  data      jsonb   NOT NULL,
 | 
				
			||||||
  script    text,
 | 
					  script    text NOT NULL,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  owner   integer REFERENCES accounts ON DELETE RESTRICT
 | 
					  owner   integer REFERENCES accounts ON DELETE RESTRICT
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										42
									
								
								server/witch/header.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								server/witch/header.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,42 @@
 | 
				
			|||||||
 | 
					package witch
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"log"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/vilmibm/hermeticum/server/db"
 | 
				
			||||||
 | 
						lua "github.com/yuin/gopher-lua"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func hasWrapper(obj db.Object) func(*lua.LState) int {
 | 
				
			||||||
 | 
						return func(ls *lua.LState) int {
 | 
				
			||||||
 | 
							lv := ls.ToTable(1)
 | 
				
			||||||
 | 
							log.Printf("%#v", lv)
 | 
				
			||||||
 | 
							return 0
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func hearsWrapper(obj db.Object) func(*lua.LState) int {
 | 
				
			||||||
 | 
						return func(ls *lua.LState) int {
 | 
				
			||||||
 | 
							// TODO get handler from _handlers
 | 
				
			||||||
 | 
							// TODO call it
 | 
				
			||||||
 | 
							// TODO how to get message in here?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return 0
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func does(ls *lua.LState) int {
 | 
				
			||||||
 | 
						// TODO
 | 
				
			||||||
 | 
						return 0
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const addHandler = `
 | 
				
			||||||
 | 
					_addHandler = function(verb, pattern, cb)
 | 
				
			||||||
 | 
						_handlers[verb] = function(message)
 | 
				
			||||||
 | 
							f, l = string.find(message, pattern)
 | 
				
			||||||
 | 
							if f != nil
 | 
				
			||||||
 | 
								cb(message)
 | 
				
			||||||
 | 
							end
 | 
				
			||||||
 | 
						end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					`
 | 
				
			||||||
							
								
								
									
										131
									
								
								server/witch/witch.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										131
									
								
								server/witch/witch.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,131 @@
 | 
				
			|||||||
 | 
					package witch
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"sync"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/vilmibm/hermeticum/server/db"
 | 
				
			||||||
 | 
						lua "github.com/yuin/gopher-lua"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						the purpose of this package is to provide abstractions for sending verbs to game objects.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Game objects get pulled from the DB into memory and their scripts become Lua States.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type scriptContext struct {
 | 
				
			||||||
 | 
						script   string
 | 
				
			||||||
 | 
						l        *lua.LState
 | 
				
			||||||
 | 
						incoming chan string
 | 
				
			||||||
 | 
						// TODO whatever is needed to support calling a Go API
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (sc *scriptContext) NeedsRefresh(obj db.Object) bool {
 | 
				
			||||||
 | 
						return sc.script != obj.Script
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// TODO using a dummy script for now
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const dummyScript = `
 | 
				
			||||||
 | 
					has({
 | 
				
			||||||
 | 
						name = "spaghetti",
 | 
				
			||||||
 | 
						description = "a plate of pasta covered in pomodoro sauce"
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					hears(".*eat.*", function(msg)
 | 
				
			||||||
 | 
						does("quivers nervously")
 | 
				
			||||||
 | 
					end)
 | 
				
			||||||
 | 
					`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					allows({
 | 
				
			||||||
 | 
					  read = "world",
 | 
				
			||||||
 | 
					  write = "owner"
 | 
				
			||||||
 | 
					  carry = "owner",
 | 
				
			||||||
 | 
					  execute = "world",
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					hears(".*eat.*", function(msg)
 | 
				
			||||||
 | 
					  does("quivers nervously")
 | 
				
			||||||
 | 
					end)
 | 
				
			||||||
 | 
					`
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// TODO figure out channel stuff
 | 
				
			||||||
 | 
					// TODO figure out how to inject WITCH header
 | 
				
			||||||
 | 
					// 	 - do i inject from Go or prepend some Lua code?
 | 
				
			||||||
 | 
					// TODO figure out how the Lua code can affect Go and thus the database
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func newScriptContext(obj db.Object) (*scriptContext, error) {
 | 
				
			||||||
 | 
						l := lua.NewState()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						l.SetGlobal("has", l.NewFunction(hasWrapper(obj)))
 | 
				
			||||||
 | 
						l.SetGlobal("_handlers", l.NewTable())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//if err := l.DoString(obj.Script); err != nil {
 | 
				
			||||||
 | 
						if err := l.DoString(dummyScript); err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return &scriptContext{}, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Gateway struct {
 | 
				
			||||||
 | 
						// maps game object IDs to script contexts
 | 
				
			||||||
 | 
						m  map[int]*scriptContext
 | 
				
			||||||
 | 
						mu sync.RWMutex
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func NewGateway() *Gateway {
 | 
				
			||||||
 | 
						return &Gateway{
 | 
				
			||||||
 | 
							m:  map[int]*scriptContext{},
 | 
				
			||||||
 | 
							mu: sync.RWMutex{},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// RefreshObj ensures that the script context for the given game object is running the latest code for the object's script
 | 
				
			||||||
 | 
					func (g *Gateway) RefreshObject(obj db.Object) error {
 | 
				
			||||||
 | 
						g.mu.RLock()
 | 
				
			||||||
 | 
						var sc *scriptContext
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if sc, ok := g.m[obj.ID]; ok {
 | 
				
			||||||
 | 
							if !sc.NeedsRefresh(obj) {
 | 
				
			||||||
 | 
								g.mu.RUnlock()
 | 
				
			||||||
 | 
								return nil
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						g.mu.RUnlock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						sc, err := newScriptContext(obj)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						g.mu.Lock()
 | 
				
			||||||
 | 
						g.m[obj.ID] = sc
 | 
				
			||||||
 | 
						g.mu.Unlock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (g *Gateway) VerbHandler(msg string, sender, target db.Object) error {
 | 
				
			||||||
 | 
						var sc *scriptContext
 | 
				
			||||||
 | 
						g.mu.RLock()
 | 
				
			||||||
 | 
						sc, ok := g.m[target.ID]
 | 
				
			||||||
 | 
						g.mu.RUnlock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if !ok || sc.NeedsRefresh(target) {
 | 
				
			||||||
 | 
							sc, err := newScriptContext(target)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							g.mu.Lock()
 | 
				
			||||||
 | 
							g.m[target.ID] = sc
 | 
				
			||||||
 | 
							g.mu.Unlock()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user