@quenty/loader 10.0.0 → 10.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,167 +0,0 @@
1
- --[=[
2
- Nevermore loader utility library
3
- @private
4
- @class GroupInfoUtils
5
- ]=]
6
-
7
- local Utils = require(script.Parent.Utils)
8
- local Queue = require(script.Parent.Queue)
9
- local LoaderConstants = require(script.Parent.LoaderConstants)
10
-
11
- local GroupInfoUtils = {}
12
-
13
- function GroupInfoUtils.createGroupInfo()
14
- return Utils.readonly({
15
- scriptInfoMap = {}; -- [name] = scriptInfo (required link packages)
16
- packageScriptInfoMap = {};
17
- packageSet = {}; -- [packageInfo] = true (actually included packages)
18
- })
19
- end
20
-
21
- function GroupInfoUtils.groupPackageInfos(packageInfoList, replicationMode)
22
- assert(type(packageInfoList) == "table", "Bad packageInfoList")
23
- assert(type(replicationMode) == "string", "Bad replicationMode")
24
-
25
- local queue = Queue.new()
26
- local seen = {}
27
-
28
- for _, packageInfo in pairs(packageInfoList) do
29
- if not seen[packageInfo] then
30
- seen[packageInfo] = true
31
- queue:PushRight(packageInfo)
32
- end
33
- end
34
-
35
- local built = {}
36
- local current = GroupInfoUtils.createGroupInfo()
37
- while not queue:IsEmpty() do
38
- local packageInfo = queue:PopLeft()
39
- if GroupInfoUtils.hasAnythingToReplicate(packageInfo, replicationMode) then
40
- if GroupInfoUtils.canAddPackageInfoToGroup(current, packageInfo, replicationMode) then
41
- GroupInfoUtils.addPackageInfoToGroup(current, packageInfo, replicationMode)
42
-
43
- if LoaderConstants.GROUP_EACH_PACKAGE_INDIVIDUALLY then
44
- table.insert(built, current)
45
- current = GroupInfoUtils.createGroupInfo()
46
- end
47
- elseif LoaderConstants.ALLOW_MULTIPLE_GROUPS then
48
- -- Create a new group
49
- table.insert(built, current)
50
- current = GroupInfoUtils.createGroupInfo()
51
- GroupInfoUtils.addPackageInfoToGroup(current, packageInfo, replicationMode)
52
- else
53
- -- Force generate error
54
- GroupInfoUtils.addPackageInfoToGroup(current, packageInfo, replicationMode)
55
-
56
- error("Cannot add package to group")
57
- end
58
- end
59
-
60
- for dependentPackageInfo, _ in pairs(packageInfo.dependencySet) do
61
- if not seen[dependentPackageInfo] then
62
- seen[dependentPackageInfo] = true
63
- queue:PushRight(dependentPackageInfo)
64
- end
65
- end
66
- end
67
-
68
- if next(current.packageSet) then
69
- table.insert(built, current)
70
- end
71
-
72
- return built
73
- end
74
-
75
- function GroupInfoUtils.hasAnythingToReplicate(packageInfo, replicationMode)
76
- return next(packageInfo.scriptInfoLookup[replicationMode]) ~= nil
77
- end
78
-
79
- function GroupInfoUtils.canAddScriptInfoToGroup(groupInfo, scriptInfo, scriptName, tempScriptInfoMap)
80
- assert(type(groupInfo) == "table", "Bad groupInfo")
81
- assert(type(scriptInfo) == "table", "Bad scriptInfo")
82
- assert(type(scriptName) == "string", "Bad scriptName")
83
- assert(type(tempScriptInfoMap) == "table", "Bad tempScriptInfoMap")
84
-
85
- local wouldHaveInfo = tempScriptInfoMap[scriptName]
86
- if wouldHaveInfo and wouldHaveInfo ~= scriptInfo then
87
- return false
88
- end
89
-
90
- local currentScriptInfo = groupInfo.scriptInfoMap[scriptName]
91
- if currentScriptInfo and currentScriptInfo ~= scriptInfo then
92
- return false
93
- end
94
-
95
- return true
96
- end
97
-
98
- function GroupInfoUtils.canAddPackageInfoToGroup(groupInfo, packageInfo, replicationMode)
99
- assert(type(groupInfo) == "table", "Bad groupInfo")
100
- assert(type(packageInfo) == "table", "Bad packageInfo")
101
- assert(type(replicationMode) == "string", "Bad replicationMode")
102
-
103
- local tempScriptInfoMap = {}
104
-
105
- -- Existing scripts must be added
106
- for scriptName, scriptInfo in pairs(packageInfo.scriptInfoLookup[replicationMode]) do
107
- if GroupInfoUtils.canAddScriptInfoToGroup(groupInfo, scriptInfo, scriptName, tempScriptInfoMap) then
108
- tempScriptInfoMap[scriptName] = scriptInfo
109
- else
110
- return false
111
- end
112
- end
113
-
114
- -- Dependencies are expected at parent level
115
- for dependencyPackageInfo, _ in pairs(packageInfo.dependencySet) do
116
- if not groupInfo.packageSet[dependencyPackageInfo] then
117
- -- Lookup dependencies and try to merge them
118
- -- O(p*d*s)
119
- for scriptName, scriptInfo in pairs(dependencyPackageInfo.scriptInfoLookup[replicationMode]) do
120
- if GroupInfoUtils.canAddScriptInfoToGroup(groupInfo, scriptInfo, scriptName, tempScriptInfoMap) then
121
- tempScriptInfoMap[scriptName] = scriptInfo
122
- else
123
- return false
124
- end
125
- end
126
- end
127
- end
128
-
129
- return true
130
- end
131
-
132
- function GroupInfoUtils.addScriptToGroup(groupInfo, scriptName, scriptInfo)
133
- assert(type(groupInfo) == "table", "Bad groupInfo")
134
- assert(type(scriptInfo) == "table", "Bad scriptInfo")
135
- assert(type(scriptName) == "string", "Bad scriptName")
136
-
137
- local currentScriptInfo = groupInfo.scriptInfoMap[scriptName]
138
- if currentScriptInfo and currentScriptInfo ~= scriptInfo then
139
- error(("Cannot add to package group, conflicting scriptInfo for %q already there")
140
- :format(scriptName))
141
- end
142
-
143
- groupInfo.scriptInfoMap[scriptName] = scriptInfo
144
- end
145
-
146
- function GroupInfoUtils.addPackageInfoToGroup(groupInfo, packageInfo, replicationMode)
147
- groupInfo.packageSet[packageInfo] = true
148
-
149
- -- Existing scripts must be added
150
- for scriptName, scriptInfo in pairs(packageInfo.scriptInfoLookup[replicationMode]) do
151
- GroupInfoUtils.addScriptToGroup(groupInfo, scriptName, scriptInfo)
152
- groupInfo.packageScriptInfoMap[scriptName] = scriptInfo
153
- end
154
-
155
- -- Dependencies are expected at parent level
156
- for dependencyPackageInfo, _ in pairs(packageInfo.dependencySet) do
157
- if not groupInfo.packageSet[dependencyPackageInfo] then
158
- -- Lookup dependencies and try to merge them
159
- -- O(p*d*s)
160
- for scriptName, scriptInfo in pairs(dependencyPackageInfo.scriptInfoLookup[replicationMode]) do
161
- GroupInfoUtils.addScriptToGroup(groupInfo, scriptName, scriptInfo)
162
- end
163
- end
164
- end
165
- end
166
-
167
- return GroupInfoUtils
@@ -1,131 +0,0 @@
1
- --[=[
2
- Legacy loading logic
3
-
4
- @private
5
- @class LegacyLoader
6
- ]=]
7
-
8
- local RunService = game:GetService("RunService")
9
- local ReplicatedStorage = game:GetService("ReplicatedStorage")
10
- local ServerScriptService = game:GetService("ServerScriptService")
11
-
12
- local LoaderUtils = require(script.Parent.LoaderUtils)
13
- local BounceTemplateUtils = require(script.Parent.BounceTemplateUtils)
14
- local Loader = require(script.Parent.Loader)
15
-
16
- local LegacyLoader = {}
17
- LegacyLoader.ClassName = "LegacyLoader"
18
- LegacyLoader.__index = LegacyLoader
19
-
20
- function LegacyLoader.new(script)
21
- return setmetatable({
22
- _script = assert(script, "No script");
23
- _container = false;
24
- _locked = false;
25
- _lookupMap = {};
26
- }, LegacyLoader)
27
- end
28
-
29
- function LegacyLoader:Lock()
30
- assert(not self._container, "Cannot bootstrap game when legacy loader was already used")
31
- self._locked = true
32
- end
33
-
34
- function LegacyLoader:GetLoader(moduleScript)
35
- return Loader.new(moduleScript)
36
- end
37
-
38
- function LegacyLoader:Require(value)
39
- assert(not self._locked, "Cannot use legacy loader after already transformed")
40
-
41
- self:_setupIfNeeded()
42
-
43
- if type(value) == "number" then
44
- return require(value)
45
- elseif type(value) == "string" then
46
- local existing = self._lookupMap[value]
47
- if existing then
48
- return require(existing)
49
- else
50
- error("Error: Library '" .. tostring(value) .. "' does not exist.", 2)
51
- end
52
- elseif typeof(value) == "Instance" and value:IsA("ModuleScript") then
53
- return require(value)
54
- else
55
- error(("Error: module must be a string or ModuleScript, got '%s' for '%s'")
56
- :format(typeof(value), tostring(value)))
57
- end
58
- end
59
-
60
- function LegacyLoader:_buildLookupContainer()
61
- for _, instance in pairs(self._container:GetDescendants()) do
62
- if instance:IsA("ModuleScript")
63
- and not instance:FindFirstAncestorWhichIsA("ModuleScript") then
64
- local target = instance
65
- if BounceTemplateUtils.isBounceTemplate(instance) then
66
- target = BounceTemplateUtils.getTarget(instance) or instance
67
- end
68
-
69
- local existing = self._lookupMap[instance.Name]
70
- if existing then
71
- if target ~= existing then
72
- warn(("[LegacyLoader] - Duplicate module %q found, using first found\n\t(1) %s (used)\n\t(2) %s")
73
- :format(
74
- instance.Name,
75
- self._lookupMap[instance.Name]:GetFullName(),
76
- instance:GetFullName()))
77
- end
78
- else
79
- self._lookupMap[instance.Name] = target
80
- end
81
- end
82
- end
83
- end
84
-
85
- function LegacyLoader:_setupIfNeeded()
86
- local existingContainer = rawget(self, "_container")
87
- if existingContainer then
88
- return existingContainer
89
- end
90
-
91
- -- TODO: Handle setup by manual process
92
- assert(self._script.Name == "Nevermore", "Cannot invoke legacy mode if not at ReplicatedStorage.Nevermore")
93
- assert(self._script.Parent == ReplicatedStorage, "Cannot invoke legacy mode if not at ReplicatedStorage.Nevermore")
94
-
95
- if not RunService:IsRunning() then
96
- error("Test mode not supported")
97
- elseif RunService:IsServer() and RunService:IsClient() or (not RunService:IsRunning()) then
98
- if RunService:IsRunning() then
99
- error("Warning: Loading all modules in PlaySolo. It's recommended you use accurate play solo.")
100
- end
101
- elseif RunService:IsServer() then
102
- local container = ServerScriptService:FindFirstChild("Nevermore") or error("No ServerScriptService.Nevermore folder")
103
- local clientFolder, serverFolder, sharedFolder = LoaderUtils.toWallyFormat(container)
104
-
105
- clientFolder.Name = "_nevermoreClient"
106
- clientFolder.Parent = ReplicatedStorage
107
-
108
- sharedFolder.Name = "_nevermoreShared"
109
- sharedFolder.Parent = ReplicatedStorage
110
-
111
- serverFolder.Name = "_nevermoreServer"
112
- serverFolder.Parent = ServerScriptService
113
-
114
- rawset(self, "_container", serverFolder)
115
- self:_buildLookupContainer()
116
- elseif RunService:IsClient() then
117
- local container = ReplicatedStorage:WaitForChild("_nevermoreClient", 2)
118
-
119
- if not container then
120
- warn("[Nevermore] - Be sure to call require(ServerScriptService.Nevermore) on the server to replicate nevermore")
121
- container = ReplicatedStorage:WaitForChild("_nevermoreClient")
122
- end
123
-
124
- rawset(self, "_container", container)
125
- self:_buildLookupContainer()
126
- else
127
- error("Error: Unknown RunService state (Not client/server/test mode)")
128
- end
129
- end
130
-
131
- return LegacyLoader
package/src/Loader.lua DELETED
@@ -1,52 +0,0 @@
1
- --[=[
2
- Loading logic for Nevermore
3
-
4
- @private
5
- @class LoaderClass
6
- ]=]
7
-
8
- local Loader = {}
9
- Loader.ClassName = "Loader"
10
- Loader.__index = Loader
11
-
12
- function Loader.new(script)
13
- return setmetatable({
14
- _script = script;
15
- _cache = {}
16
- }, Loader)
17
- end
18
-
19
- local function waitForValue(objectValue)
20
- local value = objectValue.Value
21
- if value then
22
- return value
23
- end
24
-
25
- return objectValue.Changed:Wait()
26
- end
27
-
28
- function Loader:__call(value)
29
- if type(value) == "string" then
30
- local cache = rawget(self, "_cache")
31
- if cache[value] ~= nil then
32
- return cache[value]
33
- end
34
-
35
- local object = self._script.Parent[value]
36
- if object:IsA("ObjectValue") then
37
- local result = require(waitForValue(object))
38
- cache[value] = result
39
- return result
40
- else
41
- local result = require(object)
42
- cache[value] = result
43
- return result
44
- end
45
- else
46
- return require(value)
47
- end
48
- end
49
-
50
- Loader.__index = Loader.__call
51
-
52
- return Loader
@@ -1,12 +0,0 @@
1
- --[=[
2
- @private
3
- @class LoaderConstants
4
- ]=]
5
-
6
- local Utils = require(script.Parent.Utils)
7
-
8
- return Utils.readonly({
9
- GROUP_EACH_PACKAGE_INDIVIDUALLY = false;
10
- ALLOW_MULTIPLE_GROUPS = true;
11
- INCLUDE_IMPLICIT_DEPENDENCIES = false;
12
- })
@@ -1,232 +0,0 @@
1
- --[=[
2
- Utility methods to build a virtual graph of the existing package information set
3
- @private
4
- @class PackageInfoUtils
5
- ]=]
6
-
7
- local BounceTemplateUtils = require(script.Parent.BounceTemplateUtils)
8
- local LoaderConstants = require(script.Parent.LoaderConstants)
9
- local Queue = require(script.Parent.Queue)
10
- local ScriptInfoUtils = require(script.Parent.ScriptInfoUtils)
11
- local Utils = require(script.Parent.Utils)
12
-
13
- local PackageInfoUtils = {}
14
-
15
- function PackageInfoUtils.createPackageInfo(packageFolder, explicitDependencySet, scriptInfoLookup, fullName)
16
- assert(typeof(packageFolder) == "Instance", "Bad packageFolder")
17
- assert(type(explicitDependencySet) == "table", "Bad explicitDependencySet")
18
- assert(type(scriptInfoLookup) == "table", "Bad scriptInfoLookup")
19
- assert(type(fullName) == "string", "Bad fullName")
20
-
21
- return Utils.readonly({
22
- name = packageFolder.Name;
23
- fullName = fullName;
24
- instance = packageFolder;
25
- explicitDependencySet = explicitDependencySet;
26
- dependencySet = false; -- will be filled in later, contains ALL expected dependencies
27
- scriptInfoLookup = scriptInfoLookup;
28
- })
29
- end
30
-
31
- function PackageInfoUtils.createDependencyQueueInfo(packageInfo, implicitDependencySet)
32
- assert(type(packageInfo) == "table", "Bad packageInfo")
33
- assert(type(implicitDependencySet) == "table", "Bad implicitDependencySet")
34
-
35
- return Utils.readonly({
36
- packageInfo = packageInfo;
37
- implicitDependencySet = implicitDependencySet;
38
- })
39
- end
40
-
41
- function PackageInfoUtils.fillDependencySet(packageInfoList)
42
- assert(type(packageInfoList) == "table", "Bad packageInfoList")
43
-
44
- local queue = Queue.new()
45
- local seen = {}
46
-
47
- do
48
- local topDependencySet = {}
49
- for _, packageInfo in pairs(packageInfoList) do
50
- if not seen[packageInfo] then
51
- topDependencySet[packageInfo] = packageInfo
52
- seen[packageInfo] = true
53
- queue:PushRight(PackageInfoUtils.createDependencyQueueInfo(packageInfo, topDependencySet))
54
- end
55
- end
56
- end
57
-
58
- -- Flaw: We can enter this dependency chain from multiple paths (we're a cyclic directed graph, not a tree)
59
- -- TODO: Determine node_modules behavior and copy it (hopefully any link upwards words)
60
- -- For now we do breadth first resolution of this to ensure minimal dependencies are accumulated for deep trees
61
- while not queue:IsEmpty() do
62
- local queueInfo = queue:PopLeft()
63
- assert(not queueInfo.packageInfo.dependencySet, "Already wrote dependencySet")
64
-
65
- local dependencySet = PackageInfoUtils
66
- .computePackageDependencySet(queueInfo.packageInfo, queueInfo.implicitDependencySet)
67
- queueInfo.packageInfo.dependencySet = dependencySet
68
-
69
- -- Process all explicit dependencies for the next level
70
- for packageInfo, _ in pairs(queueInfo.packageInfo.explicitDependencySet) do
71
- if not seen[packageInfo] then
72
- seen[packageInfo] = true
73
- queue:PushRight(PackageInfoUtils.createDependencyQueueInfo(packageInfo, dependencySet))
74
- end
75
- end
76
- end
77
- end
78
-
79
- function PackageInfoUtils.computePackageDependencySet(packageInfo, implicitDependencySet)
80
- assert(type(packageInfo) == "table", "Bad packageInfo")
81
- assert(type(implicitDependencySet) == "table", "Bad implicitDependencySet")
82
-
83
- -- assume folders with the same name are the same module
84
- local dependencyNameMap = {}
85
-
86
- -- Set implicit dependencies
87
- if LoaderConstants.INCLUDE_IMPLICIT_DEPENDENCIES then
88
- for entry, _ in pairs(implicitDependencySet) do
89
- dependencyNameMap[entry.name] = entry
90
- end
91
- end
92
-
93
- -- These override implicit ones
94
- for entry, _ in pairs(packageInfo.explicitDependencySet) do
95
- dependencyNameMap[entry.name] = entry
96
- end
97
-
98
- -- clear ourself as a dependency
99
- dependencyNameMap[packageInfo.name] = nil
100
-
101
- -- Note we leave conflicting scripts here as unresolved. This will output an error later.
102
- local dependencySet = {}
103
- for _, entry in pairs(dependencyNameMap) do
104
- dependencySet[entry] = true
105
- end
106
-
107
- return dependencySet
108
- end
109
-
110
- function PackageInfoUtils.getOrCreatePackageInfo(packageFolder, packageInfoMap, scope, defaultReplicationType)
111
- assert(typeof(packageFolder) == "Instance", "Bad packageFolder")
112
- assert(type(packageInfoMap) == "table", "Bad packageInfoMap")
113
- assert(type(scope) == "string", "Bad scope")
114
- assert(defaultReplicationType, "No defaultReplicationType")
115
-
116
- if packageInfoMap[packageFolder] then
117
- return packageInfoMap[packageFolder]
118
- end
119
-
120
- local scriptInfoLookup = ScriptInfoUtils.createScriptInfoLookup()
121
- ScriptInfoUtils.populateScriptInfoLookup(
122
- packageFolder,
123
- scriptInfoLookup,
124
- defaultReplicationType)
125
-
126
- local explicitDependencySet = {}
127
- local fullName
128
- if scope == "" then
129
- fullName = packageFolder.Name
130
- else
131
- fullName = scope .. "/" .. packageFolder.Name
132
- end
133
-
134
- local packageInfo = PackageInfoUtils
135
- .createPackageInfo(packageFolder, explicitDependencySet, scriptInfoLookup, fullName)
136
- packageInfoMap[packageFolder] = packageInfo
137
-
138
- -- Fill this after we've registered ourselves, in case we're somehow in a recursive dependency set
139
- PackageInfoUtils.fillExplicitPackageDependencySet(
140
- explicitDependencySet,
141
- packageFolder,
142
- packageInfoMap,
143
- defaultReplicationType)
144
-
145
- return packageInfo
146
- end
147
-
148
- function PackageInfoUtils.getPackageInfoListFromDependencyFolder(folder, packageInfoMap, defaultReplicationType)
149
- assert(typeof(folder) == "Instance" and folder:IsA("Folder"), "Bad folder")
150
- assert(type(packageInfoMap) == "table", "Bad packageInfoMap")
151
- assert(defaultReplicationType, "No defaultReplicationType")
152
-
153
-
154
- local packageInfoList = {}
155
-
156
- -- Assume we are at the dependency level
157
- for _, instance in pairs(folder:GetChildren()) do
158
- if instance:IsA("Folder") then
159
- -- We loop through each "@quenty" or "@blah" and convert to a package
160
- if instance.Name:sub(1, 1) == "@" then
161
- local scope = instance.Name
162
- for _, child in pairs(instance:GetChildren()) do
163
- PackageInfoUtils.tryLoadPackageFromInstance(packageInfoList, packageInfoMap, child, scope, defaultReplicationType)
164
- end
165
- else
166
- PackageInfoUtils.tryLoadPackageFromInstance(packageInfoList, packageInfoMap, instance, "", defaultReplicationType)
167
- end
168
- else
169
- warn(("Unknown instance in dependencyFolder - %q"):format(instance:GetFullName()))
170
- end
171
- end
172
-
173
- return packageInfoList
174
- end
175
-
176
- function PackageInfoUtils.tryLoadPackageFromInstance(
177
- packageInfoList, packageInfoMap, instance, scope, defaultReplicationType)
178
-
179
- assert(type(packageInfoList) == "table", "Bad packageInfoList")
180
- assert(type(packageInfoMap) == "table", "Bad packageInfoMap")
181
- assert(typeof(instance) == "Instance", "Bad instance")
182
- assert(type(scope) == "string", "Bad scope")
183
- assert(defaultReplicationType, "No defaultReplicationType")
184
-
185
- if BounceTemplateUtils.isBounceTemplate(instance) then
186
- return
187
- end
188
-
189
- if instance:IsA("Folder") or instance:IsA("ModuleScript") then
190
- table.insert(packageInfoList, PackageInfoUtils.getOrCreatePackageInfo(
191
- instance, packageInfoMap, scope, defaultReplicationType))
192
- elseif instance:IsA("ObjectValue") then
193
- local value = instance.Value
194
- if value and (value:IsA("Folder") or value:IsA("ModuleScript")) then
195
- table.insert(packageInfoList, PackageInfoUtils.getOrCreatePackageInfo(
196
- value, packageInfoMap, scope, defaultReplicationType))
197
- else
198
- local message = string.format("Invalid %q ObjectValue in package linking to nothing cannot be resolved into package dependency\n\t-> %s",
199
- instance.Name,
200
- instance:GetFullName())
201
- message = message .. "\n\tTIP: This happens when Rojo fails to clean out an object value. Try disconnecting Rojo and reconnecting"
202
-
203
- error(message)
204
- end
205
- end
206
- end
207
-
208
- -- Explicit dependencies are dependencies that are are explicitly listed.
209
- -- These dependencies are available to this package and ANY dependent packages below
210
- function PackageInfoUtils.fillExplicitPackageDependencySet(
211
- explicitDependencySet, packageFolder, packageInfoMap, defaultReplicationType)
212
-
213
- assert(type(explicitDependencySet) == "table", "Bad explicitDependencySet")
214
- assert(typeof(packageFolder) == "Instance", "Bad packageFolder")
215
- assert(type(packageInfoMap) == "table", "Bad packageInfoMap")
216
- assert(defaultReplicationType, "No defaultReplicationType")
217
-
218
- for _, item in pairs(packageFolder:GetChildren()) do
219
- if (item:IsA("Folder") or item:IsA("Camera")) and item.Name == ScriptInfoUtils.DEPENDENCY_FOLDER_NAME then
220
- local packageInfoList = PackageInfoUtils.getPackageInfoListFromDependencyFolder(
221
- item,
222
- packageInfoMap,
223
- defaultReplicationType)
224
-
225
- for _, packageInfo in pairs(packageInfoList) do
226
- explicitDependencySet[packageInfo] = true
227
- end
228
- end
229
- end
230
- end
231
-
232
- return PackageInfoUtils
package/src/Queue.lua DELETED
@@ -1,109 +0,0 @@
1
- --[=[
2
- Queue class with better performance characteristics than table.remove()
3
-
4
- ```lua
5
- local queue = Queue.new()
6
- queue:PushRight("a")
7
- queue:PushRight("b")
8
- queue:PushRight("c")
9
-
10
- while not queue:IsEmpty() do
11
- local entry = queue:PopLeft()
12
- print(entry) --> a, b, c
13
- end
14
- ```
15
-
16
- @class Queue
17
- ]=]
18
-
19
- local Queue = {}
20
- Queue.ClassName = "Queue"
21
- Queue.__index = Queue
22
-
23
- --[=[
24
- Constructs a new queue
25
- @return Queue<T>
26
- ]=]
27
- function Queue.new()
28
- return setmetatable({
29
- _first = 0;
30
- _last = -1;
31
- }, Queue)
32
- end
33
-
34
- --[=[
35
- Gets the queues length
36
- @return number
37
- ]=]
38
- function Queue:__len()
39
- return self._last + 1 - self._first
40
- end
41
-
42
- --[=[
43
- Returns the count of the queue
44
-
45
- @return number
46
- ]=]
47
- function Queue:GetCount()
48
- return self._last + 1 - self._first
49
- end
50
-
51
- --[=[
52
- Pushes an entry to the left of the queue
53
- @param value T
54
- ]=]
55
- function Queue:PushLeft(value)
56
- self._first = self._first - 1
57
- self[self._first] = value
58
- end
59
-
60
- --[=[
61
- Pushes an entry to the right of the queue
62
- @param value T
63
- ]=]
64
- function Queue:PushRight(value)
65
- self._last = self._last + 1
66
- self[self._last] = value
67
- end
68
-
69
- --[=[
70
- Pops an entry from the left of the queue
71
- @return T
72
- ]=]
73
- function Queue:PopLeft()
74
- if self._first > self._last then
75
- error("Queue is empty")
76
- end
77
-
78
- local value = self[self._first]
79
- self[self._first] = nil
80
- self._first = self._first + 1
81
-
82
- return value
83
- end
84
-
85
- --[=[
86
- Pops an entry from the right of the queue
87
- @return T
88
- ]=]
89
- function Queue:PopRight()
90
- if self._first > self._last then
91
- error("Queue is empty")
92
- end
93
-
94
- local value = self[self._last]
95
- self[self._last] = nil
96
- self._last = self._last - 1
97
-
98
- return value
99
- end
100
-
101
- --[=[
102
- Returns true if the queue is empty
103
- @return boolean
104
- ]=]
105
- function Queue:IsEmpty()
106
- return self._first > self._last
107
- end
108
-
109
- return Queue