lua handlers working via goroutines
parent
5456518feb
commit
ace32f9801
|
@ -104,14 +104,35 @@ func newServer() (*gameWorldServer, error) {
|
|||
}
|
||||
|
||||
func (s *gameWorldServer) verbHandler(verb, rest string, sender, target db.Object) error {
|
||||
// I think i should rethink this. sc should maybe be permanent and then they re-create LStates
|
||||
s.scriptsMutex.RLock()
|
||||
sc, ok := s.scripts[target.ID]
|
||||
s.scriptsMutex.RUnlock()
|
||||
var err error
|
||||
|
||||
sid, _ := s.db.SessionIDForAvatar(target)
|
||||
tell := func(_ int, _ string) {}
|
||||
if sid != "" {
|
||||
send := s.msgRouter[sid]
|
||||
tell = func(senderID int, msg string) {
|
||||
senderName := "a mysterious stranger"
|
||||
|
||||
sender, err := s.db.GetObjectByID(senderID)
|
||||
if err == nil {
|
||||
senderName = sender.Data["name"]
|
||||
}
|
||||
|
||||
cm := proto.ClientMessage{
|
||||
Type: proto.ClientMessage_OVERHEARD,
|
||||
Text: msg,
|
||||
Speaker: &senderName,
|
||||
}
|
||||
|
||||
send(&cm)
|
||||
}
|
||||
}
|
||||
|
||||
if !ok {
|
||||
sc, err = witch.NewScriptContext()
|
||||
sc, err = witch.NewScriptContext(tell)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -29,11 +29,13 @@ type DB interface {
|
|||
|
||||
// General
|
||||
GetObject(owner, name string) (*Object, error)
|
||||
GetObjectByID(ID int) (*Object, error)
|
||||
|
||||
// Defaults
|
||||
Ensure() error
|
||||
|
||||
// Presence
|
||||
SessionIDForAvatar(Object) (string, error)
|
||||
AvatarBySessionID(string) (*Object, error)
|
||||
BedroomBySessionID(string) (*Object, error)
|
||||
MoveInto(toMove Object, container Object) error
|
||||
|
@ -281,6 +283,26 @@ func (db *pgDB) EndSession(sid string) (err error) {
|
|||
return
|
||||
}
|
||||
|
||||
func (db *pgDB) SessionIDForAvatar(obj Object) (string, error) {
|
||||
if !obj.Avatar {
|
||||
return "", nil
|
||||
}
|
||||
fmt.Printf("%#v", obj)
|
||||
ctx := context.Background()
|
||||
stmt := `SELECT id FROM sessions WHERE account = $1`
|
||||
var sid *string
|
||||
err := db.pool.QueryRow(ctx, stmt, obj.OwnerID).Scan(&sid)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if sid == nil {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
return *sid, nil
|
||||
}
|
||||
|
||||
func (db *pgDB) AvatarBySessionID(sid string) (avatar *Object, err error) {
|
||||
avatar = &Object{}
|
||||
|
||||
|
@ -358,6 +380,18 @@ func (db *pgDB) Earshot(obj Object) ([]Object, error) {
|
|||
return out, nil
|
||||
}
|
||||
|
||||
func (db *pgDB) GetObjectByID(ID int) (*Object, error) {
|
||||
ctx := context.Background()
|
||||
obj := &Object{}
|
||||
stmt := `
|
||||
SELECT id, avatar, data, owner, script
|
||||
FROM objects
|
||||
WHERE id = $1`
|
||||
err := db.pool.QueryRow(ctx, stmt, ID).Scan(
|
||||
&obj.ID, &obj.Avatar, &obj.Data, &obj.OwnerID, &obj.Script)
|
||||
return obj, err
|
||||
}
|
||||
|
||||
func (db *pgDB) GetObject(owner, name string) (obj *Object, err error) {
|
||||
ctx := context.Background()
|
||||
obj = &Object{}
|
||||
|
|
|
@ -1,31 +1,51 @@
|
|||
package witch
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"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)
|
||||
const DefaultAvatarScript = `
|
||||
has({
|
||||
name =
|
||||
})
|
||||
`
|
||||
|
||||
func SetDefaultAvatarScript(obj *db.Object) {
|
||||
hasInvocation := "has({\n"
|
||||
for k, v := range obj.Data {
|
||||
hasInvocation += fmt.Sprintf(`%s = "%s"\n`, k, v)
|
||||
}
|
||||
hasInvocation += "})"
|
||||
|
||||
obj.Script = fmt.Sprintf(`%s
|
||||
hears(".*", function()
|
||||
tellMe(sender, msg)
|
||||
end)
|
||||
`, hasInvocation)
|
||||
|
||||
}
|
||||
|
||||
func witchHas(l *lua.LState) int {
|
||||
lv := l.ToTable(1)
|
||||
log.Println(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?
|
||||
|
||||
func witchHears(l *lua.LState) int {
|
||||
// TODO register handler
|
||||
handlers := l.GetGlobal("_handlers").(*lua.LTable)
|
||||
log.Println(handlers)
|
||||
pattern := l.ToString(1)
|
||||
cb := l.ToFunction(2)
|
||||
addHandler(l, "say", pattern, cb)
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
func does(ls *lua.LState) int {
|
||||
func witchDoes(ls *lua.LState) int {
|
||||
// TODO how to feed events back into the server?
|
||||
// it needs to behave like an event showing up in Commands stream
|
||||
// this handler needs a reference to the gateway which has a channel for sending events that the server will see?
|
||||
|
@ -52,15 +72,19 @@ func does(ls *lua.LState) int {
|
|||
}
|
||||
*/
|
||||
|
||||
func addHandler(ls *lua.LState) int {
|
||||
verb := ls.ToString(1)
|
||||
pattern := ls.ToString(2)
|
||||
cb := ls.ToFunction(3)
|
||||
handlers := ls.GetGlobal("_handlers").(*lua.LTable)
|
||||
newHandler := ls.NewTable()
|
||||
newHandler.RawSetString(pattern, cb)
|
||||
handlerMap := handlers.RawGetString(verb).(*lua.LTable)
|
||||
handlerMap.RawSetString(verb, newHandler)
|
||||
func addHandler(l *lua.LState, verb, pattern string, cb *lua.LFunction) int {
|
||||
handlers := l.GetGlobal("_handlers").(*lua.LTable)
|
||||
|
||||
verbHandlers, ok := handlers.RawGetString(verb).(*lua.LTable)
|
||||
if !ok {
|
||||
verbHandlers = l.NewTable()
|
||||
handlers.RawSetString(verb, verbHandlers)
|
||||
}
|
||||
|
||||
log.Println("addHandler")
|
||||
log.Printf("%#v", cb)
|
||||
|
||||
verbHandlers.RawSetString(pattern, cb)
|
||||
|
||||
return 0
|
||||
}
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
package witch
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"regexp"
|
||||
|
||||
"github.com/vilmibm/hermeticum/server/db"
|
||||
lua "github.com/yuin/gopher-lua"
|
||||
|
@ -23,12 +25,10 @@ has({
|
|||
description = "a plate of pasta covered in pomodoro sauce"
|
||||
})
|
||||
|
||||
hears(".*eat.*", function(msg)
|
||||
does("quivers nervously")
|
||||
end)
|
||||
|
||||
hears(".*", function(msg)
|
||||
tellMe(sender().name + " says " + msg)
|
||||
hears(".*", function()
|
||||
print(sender.name)
|
||||
print(msg)
|
||||
tellMe(msg)
|
||||
end)
|
||||
`
|
||||
|
||||
|
@ -46,11 +46,6 @@ 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
|
||||
|
||||
type VerbContext struct {
|
||||
Verb string
|
||||
Rest string
|
||||
|
@ -61,10 +56,13 @@ type VerbContext struct {
|
|||
type ScriptContext struct {
|
||||
script string
|
||||
incoming chan VerbContext
|
||||
tell func(int, string)
|
||||
}
|
||||
|
||||
func NewScriptContext() (*ScriptContext, error) {
|
||||
sc := &ScriptContext{}
|
||||
func NewScriptContext(tell func(int, string)) (*ScriptContext, error) {
|
||||
sc := &ScriptContext{
|
||||
tell: tell,
|
||||
}
|
||||
sc.incoming = make(chan VerbContext)
|
||||
|
||||
go func() {
|
||||
|
@ -78,9 +76,17 @@ func NewScriptContext() (*ScriptContext, error) {
|
|||
//sc.script = vc.Target.Script
|
||||
sc.script = dummyScript
|
||||
l = lua.NewState()
|
||||
l.SetGlobal("has", l.NewFunction(hasWrapper(vc.Target)))
|
||||
l.SetGlobal("hears", l.NewFunction(hearsWrapper(vc.Target)))
|
||||
l.SetGlobal("has", l.NewFunction(witchHas))
|
||||
l.SetGlobal("hears", l.NewFunction(witchHears))
|
||||
l.SetGlobal("_handlers", l.NewTable())
|
||||
l.SetGlobal("tellMe", l.NewFunction(func(l *lua.LState) int {
|
||||
sender := l.GetGlobal("sender").(*lua.LTable)
|
||||
senderID := int(lua.LVAsNumber(sender.RawGetString("ID")))
|
||||
msg := l.ToString(1)
|
||||
log.Printf("%#v %s\n", sender, msg)
|
||||
sc.tell(senderID, msg)
|
||||
return 0
|
||||
}))
|
||||
// TODO other setup
|
||||
//if err := l.DoString(obj.Script); err != nil {
|
||||
if err = l.DoString(dummyScript); err != nil {
|
||||
|
@ -88,7 +94,32 @@ func NewScriptContext() (*ScriptContext, error) {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO actually trigger the Lua script
|
||||
// TODO check execute permission and bail out potentially
|
||||
|
||||
senderT := l.NewTable()
|
||||
senderT.RawSetString("name", lua.LString(vc.Sender.Data["name"]))
|
||||
senderT.RawSetString("ID", lua.LString(vc.Sender.Data["ID"]))
|
||||
l.SetGlobal("sender", senderT)
|
||||
l.SetGlobal("msg", lua.LString(vc.Rest))
|
||||
|
||||
handlers := l.GetGlobal("_handlers").(*lua.LTable)
|
||||
handlers.ForEach(func(k, v lua.LValue) {
|
||||
if k.String() != vc.Verb {
|
||||
return
|
||||
}
|
||||
v.(*lua.LTable).ForEach(func(kk, vv lua.LValue) {
|
||||
pattern := regexp.MustCompile(kk.String())
|
||||
if pattern.MatchString(vc.Rest) {
|
||||
// TODO TODO TODO TODO TODO
|
||||
// this could be a remote code execution vuln; but by being here, I
|
||||
// believe vc.Verb has been effectively validated as "not a pile of
|
||||
// lua code" since it matched a handler.
|
||||
if err = l.DoString(fmt.Sprintf(`_handlers.%s["%s"]()`, vc.Verb, pattern)); err != nil {
|
||||
log.Println(err.Error())
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}()
|
||||
|
||||
|
|
Loading…
Reference in New Issue