diff --git a/server/plugins-js.go b/server/plugins-js.go index 5250e0c..04dc4bd 100644 --- a/server/plugins-js.go +++ b/server/plugins-js.go @@ -1,13 +1,14 @@ package server import ( + "fmt" "os" "github.com/dop251/goja" "github.com/dynamitemc/dynamite/logger" ) -type PluginData struct { +type pluginConfiguration struct { Identifier string `js:"identifier"` } @@ -32,7 +33,7 @@ func at[T any](arr []T, index int) (val T) { return arr[index] } -func getJavaScriptVM(logger logger.Logger, plugin *Plugin) *goja.Runtime { +func getJavaScriptVM(logger logger.Logger, plugin *Plugin, root string) *goja.Runtime { vm := goja.New() vm.SetFieldNameMapper(goja.TagFieldNameMapper("js", true)) server := vm.NewObject() @@ -63,7 +64,7 @@ func getJavaScriptVM(logger logger.Logger, plugin *Plugin) *goja.Runtime { logger.Print(format, a...) }) - vm.Set("Plugin", func(data *PluginData) { + vm.Set("Plugin", func(data *pluginConfiguration) { if data == nil { logger.Error("Failed to load plugin %s: invalid plugin data", plugin.Filename) } else { @@ -76,5 +77,20 @@ func getJavaScriptVM(logger logger.Logger, plugin *Plugin) *goja.Runtime { }) server.Set("logger", log) vm.Set("server", server) + + vm.Set("require", func(file string) map[string]interface{} { + exports := make(map[string]interface{}) + path := root + "/" + file + f, err := os.ReadFile(path) + if err != nil { + return exports + } + v := *vm + v.Set("exports", goja.Undefined()) + v.RunString(string(f)) + v.ExportTo(v.Get("exports"), &exports) + fmt.Println(v.Get("exports")) + return exports + }) return vm } diff --git a/server/plugins.go b/server/plugins.go index a0be9b0..ae0dfdb 100644 --- a/server/plugins.go +++ b/server/plugins.go @@ -1,6 +1,7 @@ package server import ( + "encoding/json" "errors" "fmt" "io/fs" @@ -11,14 +12,23 @@ import ( "github.com/dop251/goja" ) -var unrecognizedScript = errors.New("unrecognized scripting language") -var uninitalized = errors.New("plugin was not initialized") +var pluginUnrecognizedType = errors.New("unrecognized plugin type") +var pluginUninitalized = errors.New("plugin was not initialized") +var pluginNoData = errors.New("no plugin data found") +var pluginInvalidData = errors.New("failed to parse plugin data") +var pluginNoRoot = errors.New("no plugin root file") +var pluginSingleFile = errors.New("cannot import files in single file plugin") const ( PluginTypeJavaScript = iota PluginTypeLua ) +type pluginData struct { + RootFile string `json:"rootFile"` + Type string `json:"type"` +} + type Plugin struct { Identifier string Initialized bool @@ -56,42 +66,105 @@ func (srv *Server) LoadPlugins() error { } func (srv *Server) LoadPlugin(path string) (*Plugin, error) { - file, err := os.ReadFile(path) + info, err := os.Stat(path) if err != nil { fmt.Println(err) return nil, err } - sp := strings.Split(path, "/") - filename := sp[len(sp)-1] - switch { - case strings.HasSuffix(path, ".js"): - { - plugin := &Plugin{Filename: filename, Type: PluginTypeJavaScript} - js := getJavaScriptVM(srv.Logger, plugin) - plugin.JSLoader = js - _, err := js.RunString(string(file)) - if err != nil { - srv.Logger.Error("Failed to load plugin %s: %s", filename, err) - return nil, err + if info.IsDir() { + file, err := os.ReadFile(path + "/plugin.json") + var data pluginData + if err != nil { + srv.Logger.Error("Failed to load plugin %s: %s", info.Name(), pluginNoData) + return nil, pluginNoData + } + err = json.Unmarshal(file, &data) + if err != nil { + srv.Logger.Error("Failed to load plugin %s: %s", info.Name(), pluginInvalidData) + return nil, pluginInvalidData + } + file, err = os.ReadFile(path + "/" + data.RootFile) + if err != nil { + srv.Logger.Error("Failed to load plugin %s: %s", info.Name(), pluginNoRoot) + return nil, pluginNoRoot + } + switch data.Type { + case "javascript": + { + plugin := &Plugin{Filename: info.Name(), Type: PluginTypeJavaScript} + js := getJavaScriptVM(srv.Logger, plugin, path) + plugin.JSLoader = js + _, err := js.RunString(string(file)) + if err != nil { + srv.Logger.Error("Failed to load plugin %s: %s", info.Name(), err) + return nil, err + } + if !plugin.Initialized { + srv.Logger.Error("Failed to load plugin %s: %s", info.Name(), pluginUninitalized) + return nil, pluginUninitalized + } + return plugin, nil } - if !plugin.Initialized { - srv.Logger.Error("Failed to load plugin %s: %s", filename, uninitalized) - return nil, uninitalized + case "lua": + { + plugin := &Plugin{Filename: info.Name(), Type: PluginTypeLua} + l := getLuaVM(srv.Logger, plugin) + plugin.LuaLoader = l + lua.DoString(l, string(file)) + if !plugin.Initialized { + srv.Logger.Error("Failed to load plugin %s: %s", info.Name(), pluginUninitalized) + return nil, pluginUninitalized + } + return plugin, nil } - return plugin, nil + default: + { + srv.Logger.Error("Failed to load plugin %s: %s", info.Name(), pluginUnrecognizedType) + return nil, pluginUnrecognizedType + } + } + } else { + file, err := os.ReadFile(path) + if err != nil { + fmt.Println(err) + return nil, err } - case strings.HasSuffix(path, ".lua"): - { - plugin := &Plugin{Filename: filename, Type: PluginTypeLua} - l := getLuaVM(srv.Logger, plugin) - plugin.LuaLoader = l - lua.DoString(l, string(file)) - if !plugin.Initialized { - srv.Logger.Error("Failed to load plugin %s: %s", filename, uninitalized) - return nil, uninitalized + sp := strings.Split(path, "/") + filename := sp[len(sp)-1] + switch { + case strings.HasSuffix(path, ".js"): + { + plugin := &Plugin{Filename: filename, Type: PluginTypeJavaScript} + js := getJavaScriptVM(srv.Logger, plugin, "") + plugin.JSLoader = js + _, err := js.RunString(string(file)) + if err != nil { + srv.Logger.Error("Failed to load plugin %s: %s", filename, err) + return nil, err + } + if !plugin.Initialized { + srv.Logger.Error("Failed to load plugin %s: %s", filename, pluginUninitalized) + return nil, pluginUninitalized + } + return plugin, nil + } + case strings.HasSuffix(path, ".lua"): + { + plugin := &Plugin{Filename: filename, Type: PluginTypeLua} + l := getLuaVM(srv.Logger, plugin) + plugin.LuaLoader = l + lua.DoString(l, string(file)) + if !plugin.Initialized { + srv.Logger.Error("Failed to load plugin %s: %s", filename, pluginUninitalized) + return nil, pluginUninitalized + } + return plugin, nil + } + default: + { + srv.Logger.Error("Failed to load plugin %s: %s", filename, pluginUnrecognizedType) + return nil, pluginUnrecognizedType } - return plugin, nil } } - return nil, nil }