func (p *Plugin) apiAudioPlay(l *lua.State) int { if p.instance.Audio.IsPlaying() { l.PushBoolean(false) return 1 } obj := luar.NewLuaObject(l, 1) filename := obj.Get("filename").(string) callback := obj.GetObject("callback") obj.Close() if enc, ok := p.instance.Client.AudioEncoder.(*opus.Encoder); ok { enc.SetApplication(gopus.Audio) } p.instance.Audio.Source = gumble_ffmpeg.SourceFile(filename) p.instance.Audio.Play() go func() { p.instance.Audio.Wait() if callback.Type != "nil" { p.callValue(callback) } callback.Close() }() return 0 }
func pushMap(L *lua.State, m map[string]interface{}, lower bool) { L.CreateTable(0, len(m)) for k, v := range m { if lower { L.PushString(strings.ToLower(k)) } else { L.PushString(k) } switch t := v.(type) { case string: L.PushString(t) case int64: L.PushInteger(t) case int: L.PushInteger(int64(t)) case float64: L.PushNumber(t) case bool: L.PushBoolean(t) case map[string]interface{}: pushMap(L, t, false) default: L.PushNil() } L.SetTable(-3) } }
func proxy__eq(L *lua.State) int { v1, t1 := valueOfProxy(L, 1) v2, t2 := valueOfProxy(L, 2) if t1 != t2 { RaiseError(L, sprintf("mismatched types %s and %s", t1, t2)) } L.PushBoolean(v1.Interface() == v2.Interface()) return 1 }
func channel_send(L *lua.State) int { L.PushValue(2) L.PushValue(1) L.PushBoolean(true) return L.Yield(3) //~ ch,t := valueOfProxy(L,1) //~ val := valueOf(LuaToGo(L, t.Elem(),2)) //~ ch.Send(val) //~ return 0 }
func channel_recv(L *lua.State) int { L.PushValue(1) L.PushBoolean(false) return L.Yield(2) //~ ch,t := valueOfProxy(L,1) //~ L.Yield(0) //~ val,ok := ch.Recv() //~ GoToLua(L,t.Elem(),val) //~ L.PushBoolean(ok) //~ L.Resume(0) //~ return 2 }
func MessThingMovetoMethod(state *lua.State, thing *Thing) int { state.PushGoFunction(func(state *lua.State) int { source := checkThing(state, 1) target := checkThing(state, 2) ok := source.MoveTo(target) state.PushBoolean(ok) return 1 }) return 1 }
func (p *Plugin) apiAudioSetTarget(l *lua.State) int { if l.GetTop() == 0 { p.instance.Client.VoiceTarget = nil return 0 } voiceTarget, ok := luar.LuaToGo(l, nil, 1).(*gumble.VoiceTarget) if !ok { l.PushBoolean(false) return 1 } p.instance.Client.Send(voiceTarget) p.instance.Client.VoiceTarget = voiceTarget return 0 }
func initializeProxies(L *lua.State) { flagValue := func() { L.SetMetaMethod("__tostring", proxy__tostring) L.SetMetaMethod("__gc", proxy__gc) L.SetMetaMethod("__eq", proxy__eq) L.PushBoolean(true) L.SetField(-2, "luago.value") L.Pop(1) } L.NewMetaTable(cSLICE_META) L.SetMetaMethod("__index", slice__index) L.SetMetaMethod("__newindex", slice__newindex) L.SetMetaMethod("__len", slicemap__len) L.SetMetaMethod("__ipairs", slice__ipairs) flagValue() L.NewMetaTable(cMAP_META) L.SetMetaMethod("__index", map__index) L.SetMetaMethod("__newindex", map__newindex) L.SetMetaMethod("__len", slicemap__len) L.SetMetaMethod("__pairs", map__pairs) flagValue() L.NewMetaTable(cSTRUCT_META) L.SetMetaMethod("__index", struct__index) L.SetMetaMethod("__newindex", struct__newindex) flagValue() L.NewMetaTable(cINTERFACE_META) L.SetMetaMethod("__index", interface__index) flagValue() L.NewMetaTable(cCHANNEL_META) //~ RegisterFunctions(L,"*",FMap { //~ "Send":channel_send, //~ "Recv":channel_recv, //~ }) L.NewTable() L.PushGoFunction(channel_send) L.SetField(-2, "Send") L.PushGoFunction(channel_recv) L.SetField(-2, "Recv") L.SetField(-2, "__index") flagValue() }
// Push a Go value 'val' of type 't' on the Lua stack. // If we haven't been given a concrete type, use the type of the value // and unbox any interfaces. You can force slices and maps to be copied // over as tables by setting 'dontproxify' to true. func GoToLua(L *lua.State, t reflect.Type, val reflect.Value, dontproxify bool) { if !val.IsValid() { L.PushNil() return } if t == nil { t = val.Type() } if t.Kind() == reflect.Interface && !val.IsNil() { // unbox interfaces! val = valueOf(val.Interface()) t = val.Type() } if t.Kind() == reflect.Ptr { t = t.Elem() } kind := t.Kind() // underlying type is 'primitive' ? wrap it as a proxy! if isPrimitiveDerived(t, kind) != nil { makeValueProxy(L, val, cINTERFACE_META) return } switch kind { case reflect.Float64, reflect.Float32: { L.PushNumber(val.Float()) } case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: { L.PushNumber(float64(val.Int())) } case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: { L.PushNumber(float64(val.Uint())) } case reflect.String: { L.PushString(val.String()) } case reflect.Bool: { L.PushBoolean(val.Bool()) } case reflect.Slice: { if !dontproxify { makeValueProxy(L, val, cSLICE_META) } else { CopySliceToTable(L, val) } } case reflect.Map: { if !dontproxify { makeValueProxy(L, val, cMAP_META) } else { CopyMapToTable(L, val) } } case reflect.Struct: { if v, ok := val.Interface().(error); ok { L.PushString(v.Error()) } else if v, ok := val.Interface().(*LuaObject); ok { v.Push() } else { if (val.Kind() == reflect.Ptr || val.Kind() == reflect.Interface) && !val.Elem().IsValid() { L.PushNil() return } makeValueProxy(L, val, cSTRUCT_META) } } default: { if v, ok := val.Interface().(error); ok { L.PushString(v.Error()) } else if val.IsNil() { L.PushNil() } else { makeValueProxy(L, val, cINTERFACE_META) } } } }
// TODO: Check if we really need multiple pointer levels since pointer methods // can be called on non-pointers. func goToLua(L *lua.State, t reflect.Type, val reflect.Value, dontproxify bool, visited visitor) { if !val.IsValid() { L.PushNil() return } // Unbox interface. if val.Kind() == reflect.Interface && !val.IsNil() { val = reflect.ValueOf(val.Interface()) } // Follow pointers if not proxifying. We save the original pointer Value in case we proxify. ptrVal := val for val.Kind() == reflect.Ptr { val = val.Elem() } if !val.IsValid() { L.PushNil() return } // As a special case, we always proxify nullv, the empty element for slices and maps. if val.CanInterface() && val.Interface() == nullv.Interface() { makeValueProxy(L, val, cInterfaceMeta) return } switch val.Kind() { case reflect.Float64, reflect.Float32: if !dontproxify && predeclaredScalarType(val.Type()) != nil { makeValueProxy(L, ptrVal, cNumberMeta) } else { L.PushNumber(val.Float()) } case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: if !dontproxify && predeclaredScalarType(val.Type()) != nil { makeValueProxy(L, ptrVal, cNumberMeta) } else { L.PushNumber(float64(val.Int())) } case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: if !dontproxify && predeclaredScalarType(val.Type()) != nil { makeValueProxy(L, ptrVal, cNumberMeta) } else { L.PushNumber(float64(val.Uint())) } case reflect.String: if !dontproxify && predeclaredScalarType(val.Type()) != nil { makeValueProxy(L, ptrVal, cStringMeta) } else { L.PushString(val.String()) } case reflect.Bool: if !dontproxify && predeclaredScalarType(val.Type()) != nil { makeValueProxy(L, ptrVal, cInterfaceMeta) } else { L.PushBoolean(val.Bool()) } case reflect.Complex128, reflect.Complex64: makeValueProxy(L, ptrVal, cComplexMeta) case reflect.Array: // It needs be a pointer to be a proxy, otherwise values won't be settable. if !dontproxify && ptrVal.Kind() == reflect.Ptr { makeValueProxy(L, ptrVal, cSliceMeta) } else { // See the case of struct. if ptrVal.Kind() == reflect.Ptr && visited.push(ptrVal) { return } copySliceToTable(L, ptrVal, visited) } case reflect.Slice: if !dontproxify { makeValueProxy(L, ptrVal, cSliceMeta) } else { if visited.push(val) { return } copySliceToTable(L, val, visited) } case reflect.Map: if !dontproxify { makeValueProxy(L, ptrVal, cMapMeta) } else { if visited.push(val) { return } copyMapToTable(L, val, visited) } case reflect.Struct: if !dontproxify && ptrVal.Kind() == reflect.Ptr { if ptrVal.CanInterface() { switch v := ptrVal.Interface().(type) { case error: L.PushString(v.Error()) case *LuaObject: v.Push() default: makeValueProxy(L, ptrVal, cStructMeta) } } else { makeValueProxy(L, ptrVal, cStructMeta) } } else { // Use ptrVal instead of val to detect cycles from the very first element, if a pointer. if ptrVal.Kind() == reflect.Ptr && visited.push(ptrVal) { return } copyStructToTable(L, ptrVal, visited) } case reflect.Chan: makeValueProxy(L, ptrVal, cChannelMeta) case reflect.Func: L.PushGoFunction(goLuaFunc(L, val)) default: if v, ok := val.Interface().(error); ok { L.PushString(v.Error()) } else if val.IsNil() { L.PushNil() } else { makeValueProxy(L, ptrVal, cInterfaceMeta) } } }
func pushValue(state *lua.State, value interface{}) error { switch v := value.(type) { default: return fmt.Errorf("An item of unknown type was included in the environment or arguments of a Lua call (skipping it): %v", value) case nil: log.Println("Pushing nil onto lua stack") state.PushNil() case string: log.Println("Pushing string onto lua stack") state.PushString(v) case int: log.Println("Pushing int onto lua stack") state.PushInteger(int64(v)) case int64: log.Println("Pushing int64 onto lua stack") state.PushInteger(v) case float64: log.Println("Pushing float64 onto lua stack") state.PushNumber(v) case bool: log.Println("Pushing bool onto lua stack") state.PushBoolean(v) case map[string]interface{}: log.Println("Pushing map[string]interface{} onto lua stack") state.CreateTable(0, len(v)) for name, value := range v { err := pushValue(state, value) if err != nil { // error means nothing was added to stack. So pop our new table so *we* leave nothing added to the stack. state.Pop(1) return err } state.SetField(-2, name) } // then leave the table on the stack case ThingType: // These are singleton sentinel values, so load them from Lua-land. state.GetGlobal("world") state.GetField(-1, strings.Title(v.String())) state.Remove(-2) case *Thing: log.Println("Pushing *Thing onto lua stack") return pushValue(state, v.Id) case ThingId: log.Println("Pushing ThingId onto lua stack") // We're pushing a ThingId, so make a new userdata for it, with the Thing metatable. userdata := state.NewUserdata(uintptr(unsafe.Sizeof(int64(0)))) thingPtr := (*int64)(userdata) *thingPtr = int64(v) if !state.IsUserdata(-1) { log.Println("!!! HOGAD JUST PUSHED NEW USERDATA BUT IT ISN'T OMG !!!") } log.Println("Pushed ThingId", *thingPtr, "onto lua stack") // Now make it act like a Thing. state.LGetMetaTable(ThingMetaTableName) // ( udata -- udata mtbl ) state.SetMetaTable(-2) // ( udata mtbl -- udata ) // Let's just check that it's that, for sures. if !state.IsUserdata(-1) { log.Println("!!! WOOP WOOP DID NOT SET METATABLE RIGHT :( !!!") } } return nil }
func (p *Plugin) apiAudioIsPlaying(l *lua.State) int { l.PushBoolean(p.instance.Audio.IsPlaying()) return 1 }
// Push a Go value 'val' of type 't' on the Lua stack. // If we haven't been given a concrete type, use the type of the value // and unbox any interfaces. func GoToLua(L *lua.State, t reflect.Type, val reflect.Value) { proxify := true if t == nil { t = val.Type() if t.Kind() == reflect.Interface { // unbox interfaces! val = valueOf(val.Interface()) t = val.Type() } proxify = false } if t.Kind() == reflect.Ptr { t = t.Elem() } switch t.Kind() { case reflect.Float64: case reflect.Float32: { L.PushNumber(val.Float()) } case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32: { L.PushNumber(float64(val.Int())) } case reflect.Uint, reflect.Uint8: { L.PushNumber(float64(val.Uint())) } case reflect.String: { L.PushString(val.String()) } case reflect.Bool: { L.PushBoolean(val.Bool()) } case reflect.Slice: { if proxify { makeValueProxy(L, val, SLICE_META) } else { CopySliceToTable(L, val) } } case reflect.Map: { if proxify { makeValueProxy(L, val, MAP_META) } else { CopyMapToTable(L, val) } } case reflect.Struct: { makeValueProxy(L, val, STRUCT_META) } default: { if val.IsNil() { L.PushNil() } else { makeValueProxy(L, val, INTERFACE_META) } } } }