@thigasdevelopment/luam 0.1.1
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.
- package/LICENSE +21 -0
- package/README.md +616 -0
- package/lua/async.lua +119 -0
- package/lua/class.lua +178 -0
- package/lua/development-logs-client.lua +13 -0
- package/lua/development-logs-server.lua +44 -0
- package/lua/dotenv.lua +160 -0
- package/lua/env.lua +18 -0
- package/lua/math.lua +24 -0
- package/lua/string.lua +82 -0
- package/lua/table.lua +63 -0
- package/lua/threads.lua +274 -0
- package/luam.mjs +9910 -0
- package/package.json +38 -0
- package/template/README.md +120 -0
- package/template/config.lua +14 -0
- package/template/env +11 -0
- package/template/framework/bootstrap-client.luam +9 -0
- package/template/framework/bootstrap-server.luam +9 -0
- package/template/framework/command.luam +54 -0
- package/template/framework/core.luam +58 -0
- package/template/framework/event.luam +39 -0
- package/template/framework/listener.luam +40 -0
- package/template/framework/loader.luam +73 -0
- package/template/framework/thread-pool.luam +27 -0
- package/template/gitignore +7 -0
- package/template/luam.json +11 -0
- package/template/src/client/commands/ping-command.luam +8 -0
- package/template/src/client/main.luam +5 -0
- package/template/src/server/commands/hello-command.luam +9 -0
- package/template/src/server/handlers/player-join-listener.luam +9 -0
- package/template/src/server/main.luam +11 -0
- package/template/src/shared/config.luam +5 -0
- package/template/src/shared/framework/command.luam +54 -0
- package/template/src/shared/framework/core.luam +58 -0
- package/template/src/shared/framework/event.luam +39 -0
- package/template/src/shared/framework/listener.luam +40 -0
- package/template/src/shared/framework/loader.luam +73 -0
- package/template/src/shared/framework/thread-pool.luam +27 -0
package/lua/async.lua
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
---@class Async
|
|
2
|
+
---@field tasks Threads
|
|
3
|
+
---@field interval number
|
|
4
|
+
---@field map fun(self: Async, array: table, func: fun(value: any, index: number): any, callback?: fun(elapsed: number): any): number
|
|
5
|
+
---@field iterate fun(self: Async, from: number, to: number, increment: number, func: fun(i: number): any, callback?: fun(elapsed: number): any): number
|
|
6
|
+
---@field foreach fun(self: Async, object: table<any, any>, func: fun(value: any, key: any): any, callback?: fun(elapsed: number): any): number
|
|
7
|
+
---@field getInterval fun(self: Async): number
|
|
8
|
+
---@field setInterval fun(self: Async, interval: number): boolean
|
|
9
|
+
Async = {
|
|
10
|
+
---@param interval number
|
|
11
|
+
---@return Async
|
|
12
|
+
new = function (interval)
|
|
13
|
+
---@type Async
|
|
14
|
+
local self = setmetatable({ }, { __index = Async });
|
|
15
|
+
self.tasks = Threads.new ('concurrent', 'normal');
|
|
16
|
+
|
|
17
|
+
self.interval = 100;
|
|
18
|
+
self:setInterval (interval);
|
|
19
|
+
return self;
|
|
20
|
+
end,
|
|
21
|
+
|
|
22
|
+
---@param self Async
|
|
23
|
+
---@param array table
|
|
24
|
+
---@param func fun(value: any, index: number): any
|
|
25
|
+
---@param callback? fun(elapsed: number): any
|
|
26
|
+
---@return number
|
|
27
|
+
map = function (self, array, func, callback)
|
|
28
|
+
return self.tasks:add (
|
|
29
|
+
function (self)
|
|
30
|
+
local tick = getTickCount ();
|
|
31
|
+
for index, value in ipairs (array) do
|
|
32
|
+
func (value, index);
|
|
33
|
+
|
|
34
|
+
sleep (self.interval);
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
local callbackType = type (callback);
|
|
38
|
+
if (callbackType == 'function') then
|
|
39
|
+
local elapsed = (getTickCount () - tick);
|
|
40
|
+
callback (elapsed);
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
);
|
|
44
|
+
end,
|
|
45
|
+
|
|
46
|
+
---@param self Async
|
|
47
|
+
---@param from number
|
|
48
|
+
---@param to number
|
|
49
|
+
---@param increment number
|
|
50
|
+
---@param func fun(i: number): any
|
|
51
|
+
---@param callback? fun(elapsed: number): any
|
|
52
|
+
---@return number
|
|
53
|
+
iterate = function (self, from, to, increment, func, callback)
|
|
54
|
+
return self.tasks:add (
|
|
55
|
+
function (self)
|
|
56
|
+
local tick = getTickCount ();
|
|
57
|
+
for i = from, to, increment do
|
|
58
|
+
func (i);
|
|
59
|
+
|
|
60
|
+
sleep (self.interval);
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
local callbackType = type (callback);
|
|
64
|
+
if (callbackType == 'function') then
|
|
65
|
+
local elapsed = (getTickCount () - tick);
|
|
66
|
+
callback (elapsed);
|
|
67
|
+
end
|
|
68
|
+
return true;
|
|
69
|
+
end, { }, from, to, increment, func, callback
|
|
70
|
+
);
|
|
71
|
+
end,
|
|
72
|
+
|
|
73
|
+
---@param self Async
|
|
74
|
+
---@param object table<any, any>
|
|
75
|
+
---@param func fun(value: any, key: any): any
|
|
76
|
+
---@param callback? fun(elapsed: number): any
|
|
77
|
+
---@return number
|
|
78
|
+
foreach = function (self, object, func, callback)
|
|
79
|
+
return self.tasks:add (
|
|
80
|
+
function (self)
|
|
81
|
+
local tick = getTickCount ();
|
|
82
|
+
for key, value in pairs (object) do
|
|
83
|
+
func (value, key);
|
|
84
|
+
|
|
85
|
+
sleep (self.interval);
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
local callbackType = type (callback);
|
|
89
|
+
if (callbackType == 'function') then
|
|
90
|
+
local elapsed = (getTickCount () - tick);
|
|
91
|
+
callback (elapsed);
|
|
92
|
+
end
|
|
93
|
+
end, { }, object, func, callback
|
|
94
|
+
);
|
|
95
|
+
end,
|
|
96
|
+
|
|
97
|
+
---@param self Async
|
|
98
|
+
---@return number
|
|
99
|
+
getInterval = function (self)
|
|
100
|
+
return self.interval;
|
|
101
|
+
end,
|
|
102
|
+
|
|
103
|
+
---@param self Async
|
|
104
|
+
---@param interval number
|
|
105
|
+
---@return boolean
|
|
106
|
+
setInterval = function (self, interval)
|
|
107
|
+
interval = tonumber (interval);
|
|
108
|
+
if (not interval) then
|
|
109
|
+
return false;
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
if (interval < 1) or (interval == self:getInterval ()) then
|
|
113
|
+
return false;
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
self.interval = interval;
|
|
117
|
+
return true;
|
|
118
|
+
end,
|
|
119
|
+
};
|
package/lua/class.lua
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
local classes = {}
|
|
2
|
+
|
|
3
|
+
local constructors = {}
|
|
4
|
+
|
|
5
|
+
local BLOCKED_METAMETHODS = {
|
|
6
|
+
['__call'] = true,
|
|
7
|
+
['__index'] = true,
|
|
8
|
+
['__newindex'] = true,
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
local function bindSuper(value, inherited)
|
|
12
|
+
return function(self, ...)
|
|
13
|
+
local previous = rawget(self, 'super')
|
|
14
|
+
|
|
15
|
+
self.super = function(_, ...)
|
|
16
|
+
return inherited(self, ...)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
local result = value(self, ...)
|
|
20
|
+
|
|
21
|
+
self.super = previous
|
|
22
|
+
|
|
23
|
+
return result
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
local function create(name, struct, options)
|
|
28
|
+
if classes[name] then
|
|
29
|
+
error('Class ' .. tostring(name) .. ' already exists.')
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
local definition = {
|
|
33
|
+
__name = name,
|
|
34
|
+
__super = options.super,
|
|
35
|
+
__metamethods = options.metamethods,
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
for key, value in pairs(struct) do
|
|
39
|
+
local inherited = definition.__super and definition.__super[key]
|
|
40
|
+
|
|
41
|
+
if type(value) == 'function' and type(inherited) == 'function' then
|
|
42
|
+
definition[key] = bindSuper(value, inherited)
|
|
43
|
+
else
|
|
44
|
+
definition[key] = value
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
if definition.__super then
|
|
49
|
+
setmetatable(definition, { __index = definition.__super })
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
classes[name] = definition
|
|
53
|
+
|
|
54
|
+
return definition
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
function class(name)
|
|
58
|
+
local options = { super = nil, metamethods = nil }
|
|
59
|
+
|
|
60
|
+
local modifiers = {
|
|
61
|
+
extends = function(self, super)
|
|
62
|
+
options.super = classes[super]
|
|
63
|
+
|
|
64
|
+
if not options.super then
|
|
65
|
+
error('Class ' .. tostring(name) .. ' extends ' .. tostring(super) .. ', which is not defined.')
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
return self
|
|
69
|
+
end,
|
|
70
|
+
|
|
71
|
+
metamethods = function(self, metamethods)
|
|
72
|
+
options.metamethods = metamethods
|
|
73
|
+
|
|
74
|
+
return self
|
|
75
|
+
end,
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return setmetatable({}, {
|
|
79
|
+
__index = function(_, key)
|
|
80
|
+
if modifiers[key] then
|
|
81
|
+
return modifiers[key]
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
local definition = classes[name]
|
|
85
|
+
|
|
86
|
+
if not definition then
|
|
87
|
+
return nil
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
return definition[key]
|
|
91
|
+
end,
|
|
92
|
+
|
|
93
|
+
__call = function(_, struct)
|
|
94
|
+
return create(name, struct, options)
|
|
95
|
+
end,
|
|
96
|
+
})
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
local function instanceMetatable(definition)
|
|
100
|
+
local meta = { __index = definition }
|
|
101
|
+
|
|
102
|
+
if not definition.__metamethods then
|
|
103
|
+
return meta
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
for key, value in pairs(definition.__metamethods) do
|
|
107
|
+
if BLOCKED_METAMETHODS[key] then
|
|
108
|
+
error('Cannot override metamethod ' .. tostring(key) .. '.')
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
meta[key] = value
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
return meta
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
local function createConstructor(definition)
|
|
118
|
+
local meta = instanceMetatable(definition)
|
|
119
|
+
|
|
120
|
+
return function(...)
|
|
121
|
+
local instance = setmetatable({}, meta)
|
|
122
|
+
|
|
123
|
+
if type(instance.constructor) == 'function' then
|
|
124
|
+
instance:constructor(...)
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
return instance
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
function new(name)
|
|
132
|
+
local definition = classes[name]
|
|
133
|
+
|
|
134
|
+
if not definition then
|
|
135
|
+
error('Class ' .. tostring(name) .. ' is not defined.')
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
local constructor = constructors[definition]
|
|
139
|
+
|
|
140
|
+
if not constructor then
|
|
141
|
+
constructor = createConstructor(definition)
|
|
142
|
+
constructors[definition] = constructor
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
return constructor
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
function bind(func, self)
|
|
149
|
+
if type(func) ~= 'function' then
|
|
150
|
+
error('Cannot bind a value of type ' .. type(func) .. '.')
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
return function(...)
|
|
154
|
+
return func(self, ...)
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
function enum(names)
|
|
159
|
+
local values = {}
|
|
160
|
+
|
|
161
|
+
if type(names) ~= 'table' then
|
|
162
|
+
return values
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
for index = 1, #names do
|
|
166
|
+
values[names[index]] = index - 1
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
return values
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
function getClasses()
|
|
173
|
+
return classes
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
function getClass(name)
|
|
177
|
+
return classes[name]
|
|
178
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
local luamOriginalOutputDebugString = outputDebugString
|
|
2
|
+
local luamDevelopmentEvent = "luam:development-log"
|
|
3
|
+
local luamMaximumMessageLength = __LUAM_MAX_MESSAGE_LENGTH__
|
|
4
|
+
|
|
5
|
+
function outputDebugString(message, level, red, green, blue)
|
|
6
|
+
local result = luamOriginalOutputDebugString(message, level, red, green, blue)
|
|
7
|
+
|
|
8
|
+
if type(message) == "string" and #message <= luamMaximumMessageLength and (level == nil or type(level) == "number") then
|
|
9
|
+
triggerServerEvent(luamDevelopmentEvent, resourceRoot, message, level or 3)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
return result
|
|
13
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
local luamDevelopmentEvent = "luam:development-log"
|
|
2
|
+
local luamDevelopmentMarker = "__LUAM_DEV_LOG__"
|
|
3
|
+
local luamMaximumMessageLength = __LUAM_MAX_MESSAGE_LENGTH__
|
|
4
|
+
local luamRateLimit = __LUAM_RATE_LIMIT__
|
|
5
|
+
local luamRateWindow = __LUAM_RATE_WINDOW_MS__
|
|
6
|
+
local luamRateBuckets = {}
|
|
7
|
+
|
|
8
|
+
addEvent(luamDevelopmentEvent, true)
|
|
9
|
+
addEventHandler(luamDevelopmentEvent, resourceRoot, function(message, level)
|
|
10
|
+
if client == nil or source ~= resourceRoot or type(message) ~= "string" or #message > luamMaximumMessageLength then
|
|
11
|
+
return
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
if type(level) ~= "number" or level < 0 or level > 4 or level ~= math.floor(level) then
|
|
15
|
+
return
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
local now = getTickCount()
|
|
19
|
+
local bucket = luamRateBuckets[client]
|
|
20
|
+
|
|
21
|
+
if bucket == nil or now < bucket.started or now - bucket.started >= luamRateWindow then
|
|
22
|
+
bucket = { started = now, count = 0 }
|
|
23
|
+
luamRateBuckets[client] = bucket
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
if bucket.count >= luamRateLimit then
|
|
27
|
+
return
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
bucket.count = bucket.count + 1
|
|
31
|
+
|
|
32
|
+
local record = {
|
|
33
|
+
environment = "client",
|
|
34
|
+
level = level,
|
|
35
|
+
message = message,
|
|
36
|
+
resource = getResourceName(getThisResource())
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
outputDebugString(luamDevelopmentMarker .. toJSON(record, true), 4)
|
|
40
|
+
end)
|
|
41
|
+
|
|
42
|
+
addEventHandler("onPlayerQuit", root, function()
|
|
43
|
+
luamRateBuckets[source] = nil
|
|
44
|
+
end)
|
package/lua/dotenv.lua
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
local DEFAULT_FILE = '.env'
|
|
2
|
+
|
|
3
|
+
local KEYWORDS = {
|
|
4
|
+
['true'] = true,
|
|
5
|
+
['false'] = false,
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
local ESCAPES = {
|
|
9
|
+
['n'] = '\n',
|
|
10
|
+
['r'] = '\r',
|
|
11
|
+
['t'] = '\t',
|
|
12
|
+
['"'] = '"',
|
|
13
|
+
["'"] = "'",
|
|
14
|
+
['\\'] = '\\',
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
local function trim(value)
|
|
18
|
+
return value:match('^%s*(.-)%s*$') or ''
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
local function unescape(character)
|
|
22
|
+
return ESCAPES[character] or ('\\' .. character)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
local function cast(raw)
|
|
26
|
+
local quote = raw:sub(1, 1)
|
|
27
|
+
|
|
28
|
+
if quote == '"' or quote == "'" then
|
|
29
|
+
local quoted = raw:match('^' .. quote .. '(.*)' .. quote)
|
|
30
|
+
|
|
31
|
+
if quoted then
|
|
32
|
+
return (quoted:gsub('\\(.)', unescape))
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
local value = trim((raw:gsub('%s+#.*$', '')))
|
|
37
|
+
local keyword = KEYWORDS[value]
|
|
38
|
+
|
|
39
|
+
if keyword ~= nil then
|
|
40
|
+
return keyword
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
return tonumber(value) or value
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
local function read(path)
|
|
47
|
+
local file = fileOpen(path, true)
|
|
48
|
+
|
|
49
|
+
if not file then
|
|
50
|
+
return nil
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
local content = fileRead(file, fileGetSize(file))
|
|
54
|
+
|
|
55
|
+
fileClose(file)
|
|
56
|
+
|
|
57
|
+
return content
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
local function parse(path, content)
|
|
61
|
+
local values = {}
|
|
62
|
+
local number = 0
|
|
63
|
+
|
|
64
|
+
for line in (content .. '\n'):gmatch('(.-)\r?\n') do
|
|
65
|
+
number = number + 1
|
|
66
|
+
|
|
67
|
+
local trimmed = trim(line)
|
|
68
|
+
|
|
69
|
+
if trimmed ~= '' and trimmed:sub(1, 1) ~= '#' then
|
|
70
|
+
local key, raw = trimmed:match('^([%a_][%w_]*)%s*=%s*(.*)$')
|
|
71
|
+
|
|
72
|
+
if not key then
|
|
73
|
+
error('Malformed entry in "' .. path .. '" on line ' .. number .. '. Expected "KEY=value".')
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
values[key] = cast(raw)
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
return values
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
local function seal(path, values)
|
|
84
|
+
return setmetatable({}, {
|
|
85
|
+
__index = function(_, key)
|
|
86
|
+
local value = values[key]
|
|
87
|
+
|
|
88
|
+
if value == nil then
|
|
89
|
+
error('"' .. tostring(key) .. '" is not declared in "' .. path .. '".', 2)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
return value
|
|
93
|
+
end,
|
|
94
|
+
|
|
95
|
+
__newindex = function(_, key)
|
|
96
|
+
error('The environment is read-only and "' .. tostring(key) .. '" cannot be assigned.', 2)
|
|
97
|
+
end,
|
|
98
|
+
|
|
99
|
+
__metatable = false,
|
|
100
|
+
})
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
if localPlayer ~= nil then
|
|
104
|
+
return
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
Dotenv = {}
|
|
108
|
+
Dotenv.__index = Dotenv
|
|
109
|
+
|
|
110
|
+
function Dotenv.new(path)
|
|
111
|
+
path = path or DEFAULT_FILE
|
|
112
|
+
|
|
113
|
+
local content = read(path)
|
|
114
|
+
|
|
115
|
+
if not content then
|
|
116
|
+
error('Failed to read the environment file "' .. tostring(path) .. '".', 2)
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
local self = setmetatable({}, Dotenv)
|
|
120
|
+
|
|
121
|
+
self.path = path
|
|
122
|
+
self.values = parse(path, content)
|
|
123
|
+
|
|
124
|
+
return self
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
function Dotenv:get(key, default)
|
|
128
|
+
local value = self.values[key]
|
|
129
|
+
|
|
130
|
+
if value == nil then
|
|
131
|
+
return default
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
return value
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
function Dotenv:has(key)
|
|
138
|
+
return self.values[key] ~= nil
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
function Dotenv:all()
|
|
142
|
+
local copy = {}
|
|
143
|
+
|
|
144
|
+
for key, value in pairs(self.values) do
|
|
145
|
+
copy[key] = value
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
return copy
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
function Dotenv:apply()
|
|
152
|
+
if type(process) ~= 'table' then
|
|
153
|
+
process = {}
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
process.env = seal(self.path, self.values)
|
|
157
|
+
env = process.env
|
|
158
|
+
|
|
159
|
+
return process.env
|
|
160
|
+
end
|
package/lua/env.lua
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
local ENVIRONMENT_FILE = '.env'
|
|
2
|
+
|
|
3
|
+
if localPlayer ~= nil then
|
|
4
|
+
return
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
if not fileExists(ENVIRONMENT_FILE) then
|
|
8
|
+
if type(process) ~= 'table' then
|
|
9
|
+
process = {}
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
process.env = {}
|
|
13
|
+
env = process.env
|
|
14
|
+
|
|
15
|
+
return
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
Dotenv.new(ENVIRONMENT_FILE):apply()
|
package/lua/math.lua
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
function math.clamp(value, minimum, maximum)
|
|
2
|
+
value = tonumber(value)
|
|
3
|
+
|
|
4
|
+
if not value then
|
|
5
|
+
return 0
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
minimum = tonumber(minimum) or value
|
|
9
|
+
maximum = tonumber(maximum) or value
|
|
10
|
+
|
|
11
|
+
if minimum > maximum then
|
|
12
|
+
minimum, maximum = maximum, minimum
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
if value < minimum then
|
|
16
|
+
return minimum
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
if value > maximum then
|
|
20
|
+
return maximum
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
return value
|
|
24
|
+
end
|
package/lua/string.lua
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
local function trim(value)
|
|
2
|
+
if type(value) ~= 'string' then
|
|
3
|
+
return tostring(value)
|
|
4
|
+
end
|
|
5
|
+
|
|
6
|
+
return value:match('^%s*(.-)%s*$') or ''
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
local function resolve(context, path)
|
|
10
|
+
local current = context
|
|
11
|
+
|
|
12
|
+
for key in path:gmatch('[^%.]+') do
|
|
13
|
+
if type(current) ~= 'table' then
|
|
14
|
+
return nil
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
current = current[key]
|
|
18
|
+
|
|
19
|
+
if current == nil then
|
|
20
|
+
return nil
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
return current
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
local function parse(raw)
|
|
28
|
+
local key, rest = raw:match('^(.-):(.+)$')
|
|
29
|
+
|
|
30
|
+
if not key then
|
|
31
|
+
return raw, nil
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
return key, trim(rest:match('^([^:]+)'))
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
function string.trim(value)
|
|
38
|
+
return trim(value)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
function string.startsWith(value, prefix)
|
|
42
|
+
if type(value) ~= 'string' or type(prefix) ~= 'string' then
|
|
43
|
+
return false
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
return value:sub(1, prefix:len()) == prefix
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
function string.endsWith(value, suffix)
|
|
50
|
+
if type(value) ~= 'string' or type(suffix) ~= 'string' then
|
|
51
|
+
return false
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
if suffix == '' then
|
|
55
|
+
return true
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
return value:sub(-suffix:len()) == suffix
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
function string.template(text, context)
|
|
62
|
+
if type(text) ~= 'string' then
|
|
63
|
+
return tostring(text)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
if type(context) ~= 'table' or next(context) == nil then
|
|
67
|
+
return text
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
return (
|
|
71
|
+
text:gsub('%${(.-)}', function(raw)
|
|
72
|
+
local key, default = parse(trim(raw))
|
|
73
|
+
local value = resolve(context, trim(key))
|
|
74
|
+
|
|
75
|
+
if value == nil then
|
|
76
|
+
return default ~= nil and default or ''
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
return tostring(value)
|
|
80
|
+
end)
|
|
81
|
+
)
|
|
82
|
+
end
|
package/lua/table.lua
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
function table.size(value)
|
|
2
|
+
local size = 0
|
|
3
|
+
|
|
4
|
+
if type(value) ~= 'table' then
|
|
5
|
+
return size
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
for _ in pairs(value) do
|
|
9
|
+
size = size + 1
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
return size
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
function table.isEmpty(value)
|
|
16
|
+
if type(value) ~= 'table' then
|
|
17
|
+
return true
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
return next(value) == nil
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
function table.keys(value)
|
|
24
|
+
local keys = {}
|
|
25
|
+
|
|
26
|
+
if type(value) ~= 'table' then
|
|
27
|
+
return keys
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
for key in pairs(value) do
|
|
31
|
+
keys[#keys + 1] = key
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
return keys
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
function table.values(value)
|
|
38
|
+
local values = {}
|
|
39
|
+
|
|
40
|
+
if type(value) ~= 'table' then
|
|
41
|
+
return values
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
for _, item in pairs(value) do
|
|
45
|
+
values[#values + 1] = item
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
return values
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
function table.includes(value, item)
|
|
52
|
+
if type(value) ~= 'table' then
|
|
53
|
+
return false
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
for _, current in pairs(value) do
|
|
57
|
+
if current == item then
|
|
58
|
+
return true
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
return false
|
|
63
|
+
end
|