-
Notifications
You must be signed in to change notification settings - Fork 0
/
PluginManager.go
146 lines (126 loc) · 3.94 KB
/
PluginManager.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
/* PluginManager.go © Penguin_Spy 2024
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package main
import (
"log"
"os"
"path/filepath"
"time"
g "github.com/AllenDang/giu"
lua "github.com/yuin/gopher-lua"
)
type plugin struct {
id string // the plugin's internal id
L *lua.LState // the Lua state of the plugin
games []*game // games this plugin has defined. not registered until the plugin successfully finishes loading
}
var plugins = make(map[string]*plugin)
var pluginsDir string
func sleep(L *lua.LState) int {
duration := L.CheckNumber(1)
time.Sleep(time.Second * time.Duration(duration))
return 0
}
func loadLuaLib(L *lua.LState, loader lua.LGFunction) *lua.LTable {
L.Push(L.NewFunction(loader))
L.Call(0, 1)
return L.CheckTable(-1)
}
// creates a new Lua state for a plugin
func newLuaState(id string) *lua.LState {
L := lua.NewState(lua.Options{SkipOpenLibs: true})
packageLib := loadLuaLib(L, lua.OpenPackage) // TODO: probably reimplement require and remove package
packageLib.RawSetString("path", lua.LString(filepath.Join(getPluginDir(id), "?.lua")))
loadLuaLib(L, lua.OpenBase)
loadLuaLib(L, lua.OpenTable)
loadLuaLib(L, lua.OpenString)
loadLuaLib(L, lua.OpenMath)
loadLuaLib(L, lua.OpenCoroutine)
//loadLuaLib(L, lua.OpenIo)
//loadLuaLib(L, lua.OpenOs)
//loadLuaLib(L, lua.OpenDebug)
loadLuaLib(L, OpenLauncher)
//os := L.GetGlobal("os")
L.SetGlobal("sleep", L.NewFunction(sleep))
// used to determine which plugin called our API functions
L.SetField(L.Get(lua.RegistryIndex), "plugin_id", lua.LString(id))
return L
}
func loadAllPlugins() {
pluginsDir = filepath.Join(appDataPath, "plugins")
if err := os.MkdirAll(pluginsDir, os.ModeDir); err != nil {
log.Printf("[PluginManager] failed to create plugins directory! - %v\n", err)
return
}
pluginsDirEntries, err := os.ReadDir(pluginsDir)
if err != nil {
log.Printf("[PluginManager] failed to read plugins directory! - %v\n", err)
return
}
for _, entry := range pluginsDirEntries {
if !entry.IsDir() {
log.Printf("[PluginManager] ignoring non-directory item %q\n", entry)
} else {
var id = entry.Name()
log.Printf("[PluginManager] loading plugin %q\n", id)
L := newLuaState(id)
plugin := &plugin{id, L, make([]*game, 0)}
plugins[id] = plugin
if err := L.DoFile(filepath.Join(getPluginDir(id), "main.lua")); err != nil {
log.Printf("[PluginManager] error while loading plugin %q:\n%v", id, err)
L.Close()
continue
} else {
// once the plugin has successfully loaded, add its games to the list
for _, game := range plugin.games {
RegisterGame(game)
}
}
}
}
// once all plugins have loaded
if len(games) > 0 {
viewingGame = games[0]
}
guiState = guiStateGame
// only update the GUI if it's been created yet
if g.Context != nil {
g.Update()
}
}
func closeAllPlugins() {
for _, plugin := range plugins {
if !plugin.L.IsClosed() {
plugin.L.Close()
}
}
}
// gets the absolute path to the directory of the specified plugin
func getPluginDir(id string) string {
return filepath.Join(pluginsDir, id)
}
func callPluginMethod(plugin *plugin, method string, arg lua.LValue) {
L := plugin.L
function, ok := L.GetField(L.Get(lua.RegistryIndex), method).(*lua.LFunction)
if !ok {
log.Printf("[PluginManager] could not get plugin %q method %q - %v\n", plugin.id, method, function)
return
}
L.Push(function)
L.Push(arg)
// one argument, zero return values, return error normally
if err := L.PCall(1, 0, nil); err != nil {
log.Printf("[PluginManager] error while calling plugin %q method %q - %v\n", plugin.id, method, err)
}
}
func callOnPlay(game *game) {
plugin := game.plugin
t := plugin.L.NewTable()
t.RawSetString("id", lua.LString(game.id))
t.RawSetString("option", lua.LString("default"))
callPluginMethod(plugin, "on_play", t)
}