roblox-opencode 1.0.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.
- package/README.md +122 -0
- package/commands/setup-game.md +108 -0
- package/commands/sync-check.md +53 -0
- package/core/roblox-core.md +93 -0
- package/dist/server.js +167 -0
- package/package.json +35 -0
- package/skills/roblox-analytics/SKILL.md +277 -0
- package/skills/roblox-analytics/references/event-batcher.luau +75 -0
- package/skills/roblox-animation-vfx/SKILL.md +1325 -0
- package/skills/roblox-architecture/SKILL.md +863 -0
- package/skills/roblox-architecture/references/combat-systems.md +1381 -0
- package/skills/roblox-code-review/SKILL.md +687 -0
- package/skills/roblox-data/SKILL.md +889 -0
- package/skills/roblox-data/references/inventory-systems.md +1729 -0
- package/skills/roblox-debug/SKILL.md +99 -0
- package/skills/roblox-gui/SKILL.md +1103 -0
- package/skills/roblox-gui-fusion/SKILL.md +150 -0
- package/skills/roblox-gui-fusion/references/inventory.luau +427 -0
- package/skills/roblox-gui-fusion/references/settings-menu.luau +579 -0
- package/skills/roblox-gui-fusion/references/shop.luau +411 -0
- package/skills/roblox-luau-mastery/SKILL.md +1519 -0
- package/skills/roblox-monetization/SKILL.md +1084 -0
- package/skills/roblox-monetization/references/process-receipt.luau +131 -0
- package/skills/roblox-networking/SKILL.md +669 -0
- package/skills/roblox-networking/references/remote-validator.luau +193 -0
- package/skills/roblox-publish-checklist/SKILL.md +128 -0
- package/skills/roblox-runtime/SKILL.md +753 -0
- package/skills/roblox-sharp-edges/SKILL.md +295 -0
- package/skills/roblox-sync/SKILL.md +126 -0
- package/skills/roblox-testing/SKILL.md +943 -0
- package/skills/roblox-tooling/SKILL.md +150 -0
- package/vendor/LICENSES/ProfileStore-LICENSE +201 -0
- package/vendor/LICENSES/RbxUtil-LICENSE +7 -0
- package/vendor/LICENSES/promise-LICENSE +21 -0
- package/vendor/LICENSES/t-LICENSE +21 -0
- package/vendor/LICENSES/testez-LICENSE +201 -0
- package/vendor/README.md +84 -0
- package/vendor/fusion/Animation/ExternalTime.luau +84 -0
- package/vendor/fusion/Animation/Spring.luau +322 -0
- package/vendor/fusion/Animation/Stopwatch.luau +128 -0
- package/vendor/fusion/Animation/Tween.luau +187 -0
- package/vendor/fusion/Animation/getTweenDuration.luau +27 -0
- package/vendor/fusion/Animation/getTweenRatio.luau +47 -0
- package/vendor/fusion/Animation/lerpType.luau +164 -0
- package/vendor/fusion/Animation/packType.luau +100 -0
- package/vendor/fusion/Animation/springCoefficients.luau +80 -0
- package/vendor/fusion/Animation/unpackType.luau +103 -0
- package/vendor/fusion/Colour/Oklab.luau +70 -0
- package/vendor/fusion/Colour/sRGB.luau +55 -0
- package/vendor/fusion/External.luau +168 -0
- package/vendor/fusion/ExternalDebug.luau +70 -0
- package/vendor/fusion/Graph/Observer.luau +114 -0
- package/vendor/fusion/Graph/castToGraph.luau +29 -0
- package/vendor/fusion/Graph/change.luau +81 -0
- package/vendor/fusion/Graph/depend.luau +33 -0
- package/vendor/fusion/Graph/evaluate.luau +56 -0
- package/vendor/fusion/Instances/Attribute.luau +58 -0
- package/vendor/fusion/Instances/AttributeChange.luau +47 -0
- package/vendor/fusion/Instances/AttributeOut.luau +63 -0
- package/vendor/fusion/Instances/Child.luau +21 -0
- package/vendor/fusion/Instances/Children.luau +148 -0
- package/vendor/fusion/Instances/Hydrate.luau +33 -0
- package/vendor/fusion/Instances/New.luau +53 -0
- package/vendor/fusion/Instances/OnChange.luau +50 -0
- package/vendor/fusion/Instances/OnEvent.luau +54 -0
- package/vendor/fusion/Instances/Out.luau +69 -0
- package/vendor/fusion/Instances/applyInstanceProps.luau +149 -0
- package/vendor/fusion/Instances/defaultProps.luau +194 -0
- package/vendor/fusion/LICENSE +21 -0
- package/vendor/fusion/Logging/formatError.luau +49 -0
- package/vendor/fusion/Logging/messages.luau +52 -0
- package/vendor/fusion/Logging/parseError.luau +25 -0
- package/vendor/fusion/Memory/checkLifetime.luau +134 -0
- package/vendor/fusion/Memory/deriveScope.luau +24 -0
- package/vendor/fusion/Memory/deriveScopeImpl.luau +45 -0
- package/vendor/fusion/Memory/doCleanup.luau +79 -0
- package/vendor/fusion/Memory/innerScope.luau +34 -0
- package/vendor/fusion/Memory/legacyCleanup.luau +18 -0
- package/vendor/fusion/Memory/needsDestruction.luau +17 -0
- package/vendor/fusion/Memory/poisonScope.luau +34 -0
- package/vendor/fusion/Memory/scopePool.luau +55 -0
- package/vendor/fusion/Memory/scoped.luau +27 -0
- package/vendor/fusion/Memory/whichLivesLonger.luau +75 -0
- package/vendor/fusion/RobloxExternal.luau +98 -0
- package/vendor/fusion/State/Computed.luau +139 -0
- package/vendor/fusion/State/For/Disassembly.luau +211 -0
- package/vendor/fusion/State/For/ForTypes.luau +30 -0
- package/vendor/fusion/State/For/init.luau +110 -0
- package/vendor/fusion/State/ForKeys.luau +94 -0
- package/vendor/fusion/State/ForPairs.luau +97 -0
- package/vendor/fusion/State/ForValues.luau +94 -0
- package/vendor/fusion/State/Value.luau +88 -0
- package/vendor/fusion/State/castToState.luau +26 -0
- package/vendor/fusion/State/peek.luau +31 -0
- package/vendor/fusion/State/updateAll.luau +1 -0
- package/vendor/fusion/Types.luau +314 -0
- package/vendor/fusion/Utility/Contextual.luau +91 -0
- package/vendor/fusion/Utility/Safe.luau +23 -0
- package/vendor/fusion/Utility/isSimilar.luau +29 -0
- package/vendor/fusion/Utility/merge.luau +35 -0
- package/vendor/fusion/Utility/nameOf.luau +35 -0
- package/vendor/fusion/Utility/never.luau +14 -0
- package/vendor/fusion/Utility/nicknames.luau +11 -0
- package/vendor/fusion/Utility/xtypeof.luau +27 -0
- package/vendor/fusion/init.luau +82 -0
- package/vendor/profilestore/init.luau +2243 -0
- package/vendor/promise/init.luau +1982 -0
- package/vendor/rbxutil/buffer-util/Buffer.test.luau +25 -0
- package/vendor/rbxutil/buffer-util/BufferReader.luau +228 -0
- package/vendor/rbxutil/buffer-util/BufferWriter.luau +269 -0
- package/vendor/rbxutil/buffer-util/DataTypeBuffer.luau +223 -0
- package/vendor/rbxutil/buffer-util/Types.luau +60 -0
- package/vendor/rbxutil/buffer-util/index.d.ts +153 -0
- package/vendor/rbxutil/buffer-util/init.luau +41 -0
- package/vendor/rbxutil/buffer-util/package.json +16 -0
- package/vendor/rbxutil/buffer-util/wally.toml +9 -0
- package/vendor/rbxutil/comm/Client/ClientComm.luau +232 -0
- package/vendor/rbxutil/comm/Client/ClientRemoteProperty.luau +156 -0
- package/vendor/rbxutil/comm/Client/ClientRemoteSignal.luau +109 -0
- package/vendor/rbxutil/comm/Client/init.luau +135 -0
- package/vendor/rbxutil/comm/Server/RemoteProperty.luau +295 -0
- package/vendor/rbxutil/comm/Server/RemoteSignal.luau +211 -0
- package/vendor/rbxutil/comm/Server/ServerComm.luau +211 -0
- package/vendor/rbxutil/comm/Server/init.luau +140 -0
- package/vendor/rbxutil/comm/Types.luau +18 -0
- package/vendor/rbxutil/comm/Util.luau +27 -0
- package/vendor/rbxutil/comm/init.luau +35 -0
- package/vendor/rbxutil/comm/wally.toml +13 -0
- package/vendor/rbxutil/component/init.luau +759 -0
- package/vendor/rbxutil/component/init.test.luau +311 -0
- package/vendor/rbxutil/component/wally.toml +14 -0
- package/vendor/rbxutil/concur/init.luau +542 -0
- package/vendor/rbxutil/concur/init.test.luau +364 -0
- package/vendor/rbxutil/concur/wally.toml +8 -0
- package/vendor/rbxutil/enum-list/init.luau +101 -0
- package/vendor/rbxutil/enum-list/init.test.luau +91 -0
- package/vendor/rbxutil/enum-list/wally.toml +8 -0
- package/vendor/rbxutil/find/index.d.ts +20 -0
- package/vendor/rbxutil/find/init.luau +44 -0
- package/vendor/rbxutil/find/package.json +17 -0
- package/vendor/rbxutil/find/wally.toml +8 -0
- package/vendor/rbxutil/input/Gamepad.luau +559 -0
- package/vendor/rbxutil/input/Keyboard.luau +124 -0
- package/vendor/rbxutil/input/Mouse.luau +278 -0
- package/vendor/rbxutil/input/PreferredInput.luau +91 -0
- package/vendor/rbxutil/input/Touch.luau +120 -0
- package/vendor/rbxutil/input/init.luau +33 -0
- package/vendor/rbxutil/input/wally.toml +12 -0
- package/vendor/rbxutil/loader/index.d.ts +15 -0
- package/vendor/rbxutil/loader/init.luau +137 -0
- package/vendor/rbxutil/loader/wally.toml +8 -0
- package/vendor/rbxutil/log/index.d.ts +38 -0
- package/vendor/rbxutil/log/init.luau +746 -0
- package/vendor/rbxutil/log/wally.toml +8 -0
- package/vendor/rbxutil/net/init.luau +190 -0
- package/vendor/rbxutil/net/wally.toml +8 -0
- package/vendor/rbxutil/option/index.d.ts +44 -0
- package/vendor/rbxutil/option/init.luau +489 -0
- package/vendor/rbxutil/option/init.test.luau +342 -0
- package/vendor/rbxutil/option/wally.toml +8 -0
- package/vendor/rbxutil/pid/index.d.ts +53 -0
- package/vendor/rbxutil/pid/init.luau +195 -0
- package/vendor/rbxutil/pid/package.json +16 -0
- package/vendor/rbxutil/pid/wally.toml +9 -0
- package/vendor/rbxutil/quaternion/index.d.ts +117 -0
- package/vendor/rbxutil/quaternion/init.luau +570 -0
- package/vendor/rbxutil/quaternion/package.json +16 -0
- package/vendor/rbxutil/quaternion/wally.toml +9 -0
- package/vendor/rbxutil/query/index.d.ts +43 -0
- package/vendor/rbxutil/query/init.luau +117 -0
- package/vendor/rbxutil/query/package.json +18 -0
- package/vendor/rbxutil/query/wally.toml +9 -0
- package/vendor/rbxutil/sequent/index.d.ts +28 -0
- package/vendor/rbxutil/sequent/init.luau +340 -0
- package/vendor/rbxutil/sequent/package.json +16 -0
- package/vendor/rbxutil/sequent/wally.toml +9 -0
- package/vendor/rbxutil/ser/init.luau +175 -0
- package/vendor/rbxutil/ser/init.test.luau +50 -0
- package/vendor/rbxutil/ser/wally.toml +11 -0
- package/vendor/rbxutil/shake/index.d.ts +36 -0
- package/vendor/rbxutil/shake/init.luau +532 -0
- package/vendor/rbxutil/shake/init.test.luau +267 -0
- package/vendor/rbxutil/shake/package.json +16 -0
- package/vendor/rbxutil/shake/wally.toml +9 -0
- package/vendor/rbxutil/signal/index.d.ts +100 -0
- package/vendor/rbxutil/signal/init.luau +432 -0
- package/vendor/rbxutil/signal/init.test.luau +190 -0
- package/vendor/rbxutil/signal/package.json +17 -0
- package/vendor/rbxutil/signal/wally.toml +9 -0
- package/vendor/rbxutil/silo/TableWatcher.luau +65 -0
- package/vendor/rbxutil/silo/Util.luau +55 -0
- package/vendor/rbxutil/silo/init.luau +338 -0
- package/vendor/rbxutil/silo/init.test.luau +215 -0
- package/vendor/rbxutil/silo/wally.toml +8 -0
- package/vendor/rbxutil/spring/index.d.ts +40 -0
- package/vendor/rbxutil/spring/init.luau +97 -0
- package/vendor/rbxutil/spring/package.json +17 -0
- package/vendor/rbxutil/spring/wally.toml +8 -0
- package/vendor/rbxutil/stream/index.d.ts +88 -0
- package/vendor/rbxutil/stream/init.luau +597 -0
- package/vendor/rbxutil/stream/package.json +18 -0
- package/vendor/rbxutil/stream/wally.toml +9 -0
- package/vendor/rbxutil/streamable/Streamable.luau +202 -0
- package/vendor/rbxutil/streamable/StreamableUtil.luau +80 -0
- package/vendor/rbxutil/streamable/init.luau +8 -0
- package/vendor/rbxutil/streamable/wally.toml +12 -0
- package/vendor/rbxutil/symbol/init.luau +56 -0
- package/vendor/rbxutil/symbol/init.test.luau +37 -0
- package/vendor/rbxutil/symbol/wally.toml +8 -0
- package/vendor/rbxutil/table-util/init.luau +938 -0
- package/vendor/rbxutil/table-util/init.test.luau +439 -0
- package/vendor/rbxutil/table-util/wally.toml +8 -0
- package/vendor/rbxutil/task-queue/index.d.ts +27 -0
- package/vendor/rbxutil/task-queue/init.luau +97 -0
- package/vendor/rbxutil/task-queue/wally.toml +8 -0
- package/vendor/rbxutil/timer/index.d.ts +81 -0
- package/vendor/rbxutil/timer/init.luau +249 -0
- package/vendor/rbxutil/timer/init.test.luau +73 -0
- package/vendor/rbxutil/timer/wally.toml +11 -0
- package/vendor/rbxutil/tree/index.d.ts +15 -0
- package/vendor/rbxutil/tree/init.luau +137 -0
- package/vendor/rbxutil/tree/wally.toml +8 -0
- package/vendor/rbxutil/trove/index.d.ts +46 -0
- package/vendor/rbxutil/trove/init.luau +787 -0
- package/vendor/rbxutil/trove/init.test.luau +203 -0
- package/vendor/rbxutil/trove/wally.toml +8 -0
- package/vendor/rbxutil/typed-remote/init.luau +196 -0
- package/vendor/rbxutil/typed-remote/wally.toml +8 -0
- package/vendor/rbxutil/wait-for/index.d.ts +17 -0
- package/vendor/rbxutil/wait-for/init.luau +257 -0
- package/vendor/rbxutil/wait-for/init.test.luau +182 -0
- package/vendor/rbxutil/wait-for/wally.toml +11 -0
- package/vendor/t/t.lua +1350 -0
- package/vendor/testez/Context.lua +26 -0
- package/vendor/testez/Expectation.lua +311 -0
- package/vendor/testez/ExpectationContext.lua +38 -0
- package/vendor/testez/LifecycleHooks.lua +89 -0
- package/vendor/testez/Reporters/TeamCityReporter.lua +102 -0
- package/vendor/testez/Reporters/TextReporter.lua +106 -0
- package/vendor/testez/Reporters/TextReporterQuiet.lua +97 -0
- package/vendor/testez/TestBootstrap.lua +147 -0
- package/vendor/testez/TestEnum.lua +28 -0
- package/vendor/testez/TestPlan.lua +304 -0
- package/vendor/testez/TestPlanner.lua +40 -0
- package/vendor/testez/TestResults.lua +112 -0
- package/vendor/testez/TestRunner.lua +188 -0
- package/vendor/testez/TestSession.lua +243 -0
- package/vendor/testez/init.lua +40 -0
|
@@ -0,0 +1,746 @@
|
|
|
1
|
+
-- Log
|
|
2
|
+
-- Stephen Leitnick
|
|
3
|
+
-- April 20, 2021
|
|
4
|
+
|
|
5
|
+
--[[
|
|
6
|
+
|
|
7
|
+
IMPORTANT: Only make one logger per script/module
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
Log.Level { Trace, Debug, Info, Warning, Error, Fatal }
|
|
11
|
+
Log.TimeUnit { Milliseconds, Seconds, Minutes, Hours, Days, Weeks, Months, Years }
|
|
12
|
+
|
|
13
|
+
Constructor:
|
|
14
|
+
|
|
15
|
+
logger = Log.new()
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
Log:
|
|
19
|
+
|
|
20
|
+
Basic logging at levels:
|
|
21
|
+
|
|
22
|
+
logger:AtTrace():Log("Hello from trace")
|
|
23
|
+
logger:AtDebug():Log("Hello from debug")
|
|
24
|
+
logger:AtInfo():Log("Hello from info")
|
|
25
|
+
logger:AtWarning():Log("Hello from warn")
|
|
26
|
+
logger:AtError():Log("Hello from error")
|
|
27
|
+
logger:AtFatal():Log("Hello from fatal")
|
|
28
|
+
logger:At(Log.Level.Warning):Log("Warning!")
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
Log every 10 logs:
|
|
32
|
+
|
|
33
|
+
logger:AtInfo():Every(10):Log("Log this only every 10 times")
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
Log at most every 3 seconds:
|
|
37
|
+
|
|
38
|
+
logger:AtInfo():AtMostEvery(3, Log.TimeUnit.Seconds):Log("Hello there, but not too often!")
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
Wrap the Log in a function:
|
|
42
|
+
|
|
43
|
+
local log = logger:AtDebug():Wrap()
|
|
44
|
+
log("Hello")
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
--------------------------------------------------------------------------------------------------------------
|
|
48
|
+
|
|
49
|
+
LogConfig: Create a LogConfig ModuleScript anywhere in ReplicatedStorage. The configuration lets developers
|
|
50
|
+
tune the lowest logging level based on various environment conditions. The LogConfig will be automatically
|
|
51
|
+
required and used to set the log level.
|
|
52
|
+
|
|
53
|
+
To set the default configuration for all environments, simply return the log level from the LogConfig:
|
|
54
|
+
|
|
55
|
+
return "Info"
|
|
56
|
+
|
|
57
|
+
To set a configuration that is different while in Studio:
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
Studio = "Debug";
|
|
61
|
+
Other = "Warning"; -- "Other" can be anything other than Studio (e.g. could be named "Default")
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
Fine-tune between server and client:
|
|
65
|
+
|
|
66
|
+
return {
|
|
67
|
+
Studio = {
|
|
68
|
+
Server = "Info";
|
|
69
|
+
Client = "Debug";
|
|
70
|
+
};
|
|
71
|
+
Other = "Warning";
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
Fine-tune based on PlaceIds:
|
|
75
|
+
|
|
76
|
+
return {
|
|
77
|
+
Studio = {
|
|
78
|
+
Server = "Info";
|
|
79
|
+
Client = "Debug";
|
|
80
|
+
};
|
|
81
|
+
Other = {
|
|
82
|
+
PlaceIds = {123456, 234567}
|
|
83
|
+
Server = "Severe";
|
|
84
|
+
Client = "Warning";
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
Fine-tune based on GameIds:
|
|
89
|
+
|
|
90
|
+
return {
|
|
91
|
+
Studio = {
|
|
92
|
+
Server = "Info";
|
|
93
|
+
Client = "Debug";
|
|
94
|
+
};
|
|
95
|
+
Other = {
|
|
96
|
+
GameIds = {123456, 234567}
|
|
97
|
+
Server = "Severe";
|
|
98
|
+
Client = "Warning";
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
Example of full-scale config with multiple environments:
|
|
103
|
+
|
|
104
|
+
return {
|
|
105
|
+
Studio = {
|
|
106
|
+
Server = "Debug";
|
|
107
|
+
Client = "Debug";
|
|
108
|
+
};
|
|
109
|
+
Dev = {
|
|
110
|
+
PlaceIds = {1234567};
|
|
111
|
+
Server = "Info";
|
|
112
|
+
Client = "Info";
|
|
113
|
+
};
|
|
114
|
+
Prod = {
|
|
115
|
+
PlaceIds = {2345678};
|
|
116
|
+
Server = "Severe";
|
|
117
|
+
Client = "Warning";
|
|
118
|
+
};
|
|
119
|
+
Default = "Info";
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
--]]
|
|
123
|
+
|
|
124
|
+
local IS_STUDIO = game:GetService("RunService"):IsStudio()
|
|
125
|
+
local IS_SERVER = game:GetService("RunService"):IsServer()
|
|
126
|
+
|
|
127
|
+
local AnalyticsService = game:GetService("AnalyticsService")
|
|
128
|
+
local HttpService = game:GetService("HttpService")
|
|
129
|
+
local Players = game:GetService("Players")
|
|
130
|
+
local AnalyticsLogLevel = Enum.AnalyticsLogLevel
|
|
131
|
+
|
|
132
|
+
local player = (not IS_SERVER and Players.LocalPlayer or nil)
|
|
133
|
+
|
|
134
|
+
local configModule = game:GetService("ReplicatedStorage"):FindFirstChild("LogConfig", true)
|
|
135
|
+
local config = (configModule and require(configModule) or "Debug")
|
|
136
|
+
|
|
137
|
+
local logLevel = nil
|
|
138
|
+
local timeFunc = os.clock
|
|
139
|
+
|
|
140
|
+
local logLevels = {
|
|
141
|
+
Trace = AnalyticsLogLevel.Trace.Value,
|
|
142
|
+
Debug = AnalyticsLogLevel.Debug.Value,
|
|
143
|
+
Info = AnalyticsLogLevel.Information.Value,
|
|
144
|
+
Warning = AnalyticsLogLevel.Warning.Value,
|
|
145
|
+
Error = AnalyticsLogLevel.Error.Value,
|
|
146
|
+
Fatal = AnalyticsLogLevel.Fatal.Value,
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
local timeUnits = {
|
|
150
|
+
Milliseconds = 0,
|
|
151
|
+
Seconds = 1,
|
|
152
|
+
Minutes = 2,
|
|
153
|
+
Hours = 3,
|
|
154
|
+
Days = 4,
|
|
155
|
+
Weeks = 5,
|
|
156
|
+
Months = 6,
|
|
157
|
+
Years = 7,
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
local function ToSeconds(n, timeUnit)
|
|
161
|
+
if timeUnit == timeUnits.Milliseconds then
|
|
162
|
+
return n / 1000
|
|
163
|
+
elseif timeUnit == timeUnits.Seconds then
|
|
164
|
+
return n
|
|
165
|
+
elseif timeUnit == timeUnits.Minutes then
|
|
166
|
+
return n * 60
|
|
167
|
+
elseif timeUnit == timeUnits.Hours then
|
|
168
|
+
return n * 3600
|
|
169
|
+
elseif timeUnit == timeUnits.Days then
|
|
170
|
+
return n * 86400
|
|
171
|
+
elseif timeUnit == timeUnits.Weeks then
|
|
172
|
+
return n * 604800
|
|
173
|
+
elseif timeUnit == timeUnits.Months then
|
|
174
|
+
return n * 2592000
|
|
175
|
+
elseif timeUnit == timeUnits.Years then
|
|
176
|
+
return n * 31536000
|
|
177
|
+
else
|
|
178
|
+
error("Unknown time unit", 2)
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
local function GetPlayerFromCustomData(customData)
|
|
183
|
+
if type(customData) == "table" then
|
|
184
|
+
local id = (customData.Player or customData.PlayerId)
|
|
185
|
+
if id then
|
|
186
|
+
return Players:GetPlayerByUserId(id)
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
return nil
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
local FireAnalyticsLogEvent
|
|
193
|
+
if IS_STUDIO then
|
|
194
|
+
FireAnalyticsLogEvent = function(_level, _message, _traceback, _customData) end
|
|
195
|
+
else
|
|
196
|
+
FireAnalyticsLogEvent = function(level, message, traceback, customData)
|
|
197
|
+
local success, err = pcall(function()
|
|
198
|
+
local plr = (player or GetPlayerFromCustomData(customData))
|
|
199
|
+
AnalyticsService:FireLogEvent(plr, level, message, { stackTrace = traceback }, customData)
|
|
200
|
+
end)
|
|
201
|
+
if not success then
|
|
202
|
+
warn(err)
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
local LogItem = {}
|
|
208
|
+
LogItem.__index = LogItem
|
|
209
|
+
|
|
210
|
+
function LogItem.new(log, levelName, traceback, key)
|
|
211
|
+
local self = setmetatable({
|
|
212
|
+
_log = log,
|
|
213
|
+
_traceback = traceback,
|
|
214
|
+
_levelName = levelName,
|
|
215
|
+
_modifiers = {
|
|
216
|
+
Throw = false,
|
|
217
|
+
},
|
|
218
|
+
_key = key,
|
|
219
|
+
}, LogItem)
|
|
220
|
+
return self
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
function LogItem:_shouldLog(stats)
|
|
224
|
+
if self._modifiers.Every and not stats:_checkAndIncrementCount(self._modifiers.Every) then
|
|
225
|
+
return false
|
|
226
|
+
end
|
|
227
|
+
if self._modifiers.AtMostEvery and not stats:_checkLastTimestamp(timeFunc(), self._modifiers.AtMostEvery) then
|
|
228
|
+
return false
|
|
229
|
+
end
|
|
230
|
+
return true
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
function LogItem:Every(n)
|
|
234
|
+
self._modifiers.Every = n
|
|
235
|
+
return self
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
function LogItem:AtMostEvery(n, timeUnit)
|
|
239
|
+
self._modifiers.AtMostEvery = ToSeconds(n, timeUnit)
|
|
240
|
+
return self
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
function LogItem:Throw()
|
|
244
|
+
self._modifiers.Throw = true
|
|
245
|
+
return self
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
function LogItem:Log(message, customData)
|
|
249
|
+
local stats = self._log:_getLogStats(self._key)
|
|
250
|
+
if not self:_shouldLog(stats) then
|
|
251
|
+
return
|
|
252
|
+
end
|
|
253
|
+
if type(message) == "function" then
|
|
254
|
+
local msg, data = message()
|
|
255
|
+
message = msg
|
|
256
|
+
if data ~= nil then
|
|
257
|
+
customData = data
|
|
258
|
+
end
|
|
259
|
+
elseif type(message) == "table" then
|
|
260
|
+
message = HttpService:JSONEncode(message)
|
|
261
|
+
end
|
|
262
|
+
stats:_setTimestamp(timeFunc())
|
|
263
|
+
local logMessage = ("%s: [%s] %s"):format(self._log._name, self._levelName, message)
|
|
264
|
+
local logLevelNum = logLevels[self._levelName]
|
|
265
|
+
FireAnalyticsLogEvent(logLevelNum, ("%s: %s"):format(self._log._name, message), self._traceback, customData)
|
|
266
|
+
if self._modifiers.Throw then
|
|
267
|
+
error(logMessage .. (customData and (" " .. HttpService:JSONEncode(customData)) or ""), 4)
|
|
268
|
+
elseif logLevelNum < logLevels.Warning then
|
|
269
|
+
print(logMessage, customData or "")
|
|
270
|
+
else
|
|
271
|
+
warn(logMessage, customData or "")
|
|
272
|
+
end
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
function LogItem:Wrap()
|
|
276
|
+
return function(...)
|
|
277
|
+
self:Log(...)
|
|
278
|
+
end
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
function LogItem:Assert(condition, ...)
|
|
282
|
+
if condition then
|
|
283
|
+
self:Throw():Log(...)
|
|
284
|
+
end
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
local LogItemBlank = {}
|
|
288
|
+
LogItemBlank.__index = LogItemBlank
|
|
289
|
+
setmetatable(LogItemBlank, LogItem)
|
|
290
|
+
|
|
291
|
+
function LogItemBlank.new(...)
|
|
292
|
+
local self = setmetatable(LogItem.new(...), LogItemBlank)
|
|
293
|
+
return self
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
function LogItemBlank:Log()
|
|
297
|
+
-- Do nothing
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
local LogStats = {}
|
|
301
|
+
LogStats.__index = LogStats
|
|
302
|
+
|
|
303
|
+
function LogStats.new()
|
|
304
|
+
local self = setmetatable({}, LogStats)
|
|
305
|
+
self._invocationCount = 0
|
|
306
|
+
self._lastTimestamp = 0
|
|
307
|
+
return self
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
function LogStats:_checkAndIncrementCount(rateLimit)
|
|
311
|
+
local check = ((self._invocationCount % rateLimit) == 0)
|
|
312
|
+
self._invocationCount += 1
|
|
313
|
+
return check
|
|
314
|
+
end
|
|
315
|
+
|
|
316
|
+
function LogStats:_checkLastTimestamp(now, intervalSeconds)
|
|
317
|
+
return ((now - self._lastTimestamp) >= intervalSeconds)
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
function LogStats:_setTimestamp(now)
|
|
321
|
+
self._lastTimestamp = now
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
--[=[
|
|
325
|
+
@class Log
|
|
326
|
+
@server
|
|
327
|
+
Log class for logging to the AnalyticsService (e.g. PlayFab). The API
|
|
328
|
+
is based off of Google's [Flogger](https://google.github.io/flogger/)
|
|
329
|
+
fluent logging API.
|
|
330
|
+
|
|
331
|
+
```lua
|
|
332
|
+
local Log = require(somewhere.Log)
|
|
333
|
+
local logger = Log.new()
|
|
334
|
+
|
|
335
|
+
-- Log a simple message:
|
|
336
|
+
logger:AtInfo():Log("Hello world!")
|
|
337
|
+
|
|
338
|
+
-- Log only every 3 messages:
|
|
339
|
+
for i = 1,20 do
|
|
340
|
+
logger:AtInfo():Every(3):Log("Hi there!")
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
-- Log only every 1 second:
|
|
344
|
+
for i = 1,100 do
|
|
345
|
+
logger:AtInfo():AtMostEvery(3, Log.TimeUnit.Seconds):Log("Hello!")
|
|
346
|
+
task.wait(0.1)
|
|
347
|
+
end
|
|
348
|
+
|
|
349
|
+
-- Wrap the above example into a function:
|
|
350
|
+
local log = logger:AtInfo():AtMostEvery(3, Log.TimeUnit.Seconds):Wrap()
|
|
351
|
+
for i = 1,100 do
|
|
352
|
+
log("Hello!")
|
|
353
|
+
task.wait(0.1)
|
|
354
|
+
end
|
|
355
|
+
|
|
356
|
+
-- Assertion:
|
|
357
|
+
logger:Assert(typeof(32) == "number", "Somehow 32 is no longer a number")
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
------------
|
|
361
|
+
|
|
362
|
+
### LogConfig
|
|
363
|
+
|
|
364
|
+
A LogConfig ModuleScript is expected to exist somewhere within ReplicatedStorage
|
|
365
|
+
as well. This ModuleScript defines the behavior for the logger. If not found,
|
|
366
|
+
the logger will default to the Debug log level for all operations.
|
|
367
|
+
|
|
368
|
+
For instance, this could be a script located at `ReplicatedStorage.MyGameConfig.LogConfig`. There
|
|
369
|
+
just needs to be some `LogConfig`-named ModuleScript within ReplicatedStorage.
|
|
370
|
+
|
|
371
|
+
Below are a few examples of possible LogConfig ModuleScripts:
|
|
372
|
+
|
|
373
|
+
```lua
|
|
374
|
+
-- Set "Info" as default log level for all environments:
|
|
375
|
+
return "Info"
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
```lua
|
|
379
|
+
-- To set a configuration that is different while in Studio:
|
|
380
|
+
return {
|
|
381
|
+
Studio = "Debug";
|
|
382
|
+
Other = "Warning"; -- "Other" can be anything other than Studio (e.g. could be named "Default")
|
|
383
|
+
}
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
```lua
|
|
387
|
+
-- Fine-tune between server and client:
|
|
388
|
+
return {
|
|
389
|
+
Studio = {
|
|
390
|
+
Server = "Info";
|
|
391
|
+
Client = "Debug";
|
|
392
|
+
};
|
|
393
|
+
Other = "Warning";
|
|
394
|
+
}
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
```lua
|
|
398
|
+
-- Fine-tune based on PlaceIds:
|
|
399
|
+
return {
|
|
400
|
+
Studio = {
|
|
401
|
+
Server = "Info";
|
|
402
|
+
Client = "Debug";
|
|
403
|
+
};
|
|
404
|
+
Other = {
|
|
405
|
+
PlaceIds = {123456, 234567}
|
|
406
|
+
Server = "Severe";
|
|
407
|
+
Client = "Warning";
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
```lua
|
|
413
|
+
-- Fine-tune based on GameIds:
|
|
414
|
+
return {
|
|
415
|
+
Studio = {
|
|
416
|
+
Server = "Info";
|
|
417
|
+
Client = "Debug";
|
|
418
|
+
};
|
|
419
|
+
Other = {
|
|
420
|
+
GameIds = {123456, 234567}
|
|
421
|
+
Server = "Severe";
|
|
422
|
+
Client = "Warning";
|
|
423
|
+
};
|
|
424
|
+
}
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
```lua
|
|
428
|
+
-- Example of full-scale config with multiple environments:
|
|
429
|
+
return {
|
|
430
|
+
Studio = {
|
|
431
|
+
Server = "Debug";
|
|
432
|
+
Client = "Debug";
|
|
433
|
+
};
|
|
434
|
+
Dev = {
|
|
435
|
+
PlaceIds = {1234567};
|
|
436
|
+
Server = "Info";
|
|
437
|
+
Client = "Info";
|
|
438
|
+
};
|
|
439
|
+
Prod = {
|
|
440
|
+
PlaceIds = {2345678};
|
|
441
|
+
Server = "Severe";
|
|
442
|
+
Client = "Warning";
|
|
443
|
+
};
|
|
444
|
+
Default = "Info";
|
|
445
|
+
}
|
|
446
|
+
```
|
|
447
|
+
]=]
|
|
448
|
+
local Log = {}
|
|
449
|
+
Log.__index = Log
|
|
450
|
+
|
|
451
|
+
--[=[
|
|
452
|
+
@within Log
|
|
453
|
+
@interface LogItem
|
|
454
|
+
.Log (message: any, customData: table?) -- Log the message
|
|
455
|
+
.Every (n: number) -- Log only every `n` times
|
|
456
|
+
.AtMostEvery (n: number, timeUnit: TimeUnit) -- Log only every `n` `TimeUnit`
|
|
457
|
+
.Throw () -- Throw an error
|
|
458
|
+
.Wrap () -- Returns a function that can be called which will log out the given arguments
|
|
459
|
+
.Assert (condition: boolean, args: ...) -- Assert the condition
|
|
460
|
+
]=]
|
|
461
|
+
|
|
462
|
+
--[=[
|
|
463
|
+
@within Log
|
|
464
|
+
@interface TimeUnit
|
|
465
|
+
.Milliseconds number
|
|
466
|
+
.Seeconds number
|
|
467
|
+
.Minutes number
|
|
468
|
+
.Hours number
|
|
469
|
+
.Days number
|
|
470
|
+
.Weeks number
|
|
471
|
+
.Months number
|
|
472
|
+
.Years number
|
|
473
|
+
]=]
|
|
474
|
+
|
|
475
|
+
--[=[
|
|
476
|
+
@within Log
|
|
477
|
+
@interface Level
|
|
478
|
+
.Trace number
|
|
479
|
+
.Debug number
|
|
480
|
+
.Info number
|
|
481
|
+
.Warning number
|
|
482
|
+
.Error number
|
|
483
|
+
.Fatal number
|
|
484
|
+
]=]
|
|
485
|
+
|
|
486
|
+
--[=[
|
|
487
|
+
@within Log
|
|
488
|
+
@prop TimeUnit TimeUnit
|
|
489
|
+
@readonly
|
|
490
|
+
]=]
|
|
491
|
+
|
|
492
|
+
--[=[
|
|
493
|
+
@within Log
|
|
494
|
+
@prop Level Level
|
|
495
|
+
@readonly
|
|
496
|
+
]=]
|
|
497
|
+
|
|
498
|
+
Log.TimeUnit = timeUnits
|
|
499
|
+
Log.Level = logLevels
|
|
500
|
+
|
|
501
|
+
Log.LevelNames = {}
|
|
502
|
+
for name, num in pairs(Log.Level) do
|
|
503
|
+
Log.LevelNames[num] = name
|
|
504
|
+
end
|
|
505
|
+
|
|
506
|
+
--[=[
|
|
507
|
+
@return Log
|
|
508
|
+
Construct a new Log object.
|
|
509
|
+
|
|
510
|
+
:::warning
|
|
511
|
+
This should only be called once per script.
|
|
512
|
+
:::
|
|
513
|
+
]=]
|
|
514
|
+
function Log.new()
|
|
515
|
+
local self = setmetatable({}, Log)
|
|
516
|
+
local name = debug.info(2, "s"):match("([^%.]-)$")
|
|
517
|
+
self._name = name
|
|
518
|
+
self._stats = {}
|
|
519
|
+
return self
|
|
520
|
+
end
|
|
521
|
+
|
|
522
|
+
function Log:_getLogStats(key)
|
|
523
|
+
local stats = self._stats[key]
|
|
524
|
+
if not stats then
|
|
525
|
+
stats = LogStats.new()
|
|
526
|
+
self._stats[key] = stats
|
|
527
|
+
end
|
|
528
|
+
return stats
|
|
529
|
+
end
|
|
530
|
+
|
|
531
|
+
function Log:_at(level)
|
|
532
|
+
local l, f = debug.info(3, "lf")
|
|
533
|
+
local traceback = debug.traceback("Log", 3)
|
|
534
|
+
local key = (tostring(l) .. tostring(f))
|
|
535
|
+
if level < logLevel then
|
|
536
|
+
return LogItemBlank.new(self, Log.LevelNames[level], traceback, key)
|
|
537
|
+
else
|
|
538
|
+
return LogItem.new(self, Log.LevelNames[level], traceback, key)
|
|
539
|
+
end
|
|
540
|
+
end
|
|
541
|
+
|
|
542
|
+
--[=[
|
|
543
|
+
@param level LogLevel
|
|
544
|
+
@return LogItem
|
|
545
|
+
]=]
|
|
546
|
+
function Log:At(level)
|
|
547
|
+
return self:_at(level)
|
|
548
|
+
end
|
|
549
|
+
|
|
550
|
+
--[=[
|
|
551
|
+
@return LogItem
|
|
552
|
+
Get a LogItem at the Trace log level.
|
|
553
|
+
]=]
|
|
554
|
+
function Log:AtTrace()
|
|
555
|
+
return self:_at(Log.Level.Trace)
|
|
556
|
+
end
|
|
557
|
+
|
|
558
|
+
--[=[
|
|
559
|
+
@return LogItem
|
|
560
|
+
Get a LogItem at the Debug log level.
|
|
561
|
+
]=]
|
|
562
|
+
function Log:AtDebug()
|
|
563
|
+
return self:_at(Log.Level.Debug)
|
|
564
|
+
end
|
|
565
|
+
|
|
566
|
+
--[=[
|
|
567
|
+
@return LogItem
|
|
568
|
+
Get a LogItem at the Info log level.
|
|
569
|
+
]=]
|
|
570
|
+
function Log:AtInfo()
|
|
571
|
+
return self:_at(Log.Level.Info)
|
|
572
|
+
end
|
|
573
|
+
|
|
574
|
+
--[=[
|
|
575
|
+
@return LogItem
|
|
576
|
+
Get a LogItem at the Warning log level.
|
|
577
|
+
]=]
|
|
578
|
+
function Log:AtWarning()
|
|
579
|
+
return self:_at(Log.Level.Warning)
|
|
580
|
+
end
|
|
581
|
+
|
|
582
|
+
--[=[
|
|
583
|
+
@return LogItem
|
|
584
|
+
Get a LogItem at the Error log level.
|
|
585
|
+
]=]
|
|
586
|
+
function Log:AtError()
|
|
587
|
+
return self:_at(Log.Level.Error)
|
|
588
|
+
end
|
|
589
|
+
|
|
590
|
+
--[=[
|
|
591
|
+
@return LogItem
|
|
592
|
+
Get a LogItem at the Fatal log level.
|
|
593
|
+
]=]
|
|
594
|
+
function Log:AtFatal()
|
|
595
|
+
return self:_at(Log.Level.Fatal)
|
|
596
|
+
end
|
|
597
|
+
|
|
598
|
+
--[=[
|
|
599
|
+
@param condition boolean
|
|
600
|
+
@param ... any
|
|
601
|
+
Asserts the condition and then logs the following
|
|
602
|
+
arguments at the Error level if the condition
|
|
603
|
+
fails.
|
|
604
|
+
]=]
|
|
605
|
+
function Log:Assert(condition, ...)
|
|
606
|
+
if not condition then
|
|
607
|
+
self:_at(Log.Level.Error):Throw():Log(...)
|
|
608
|
+
end
|
|
609
|
+
end
|
|
610
|
+
|
|
611
|
+
function Log:Destroy() end
|
|
612
|
+
|
|
613
|
+
function Log:__tostring()
|
|
614
|
+
return ("Log<%s>"):format(self._name)
|
|
615
|
+
end
|
|
616
|
+
|
|
617
|
+
-- Determine log level:
|
|
618
|
+
do
|
|
619
|
+
local function SetLogLevel(name)
|
|
620
|
+
local n = name:lower()
|
|
621
|
+
for levelName, level in pairs(Log.Level) do
|
|
622
|
+
if levelName:lower() == n then
|
|
623
|
+
if IS_STUDIO then
|
|
624
|
+
local attr = (IS_SERVER and "LogLevel" or "LogLevelClient")
|
|
625
|
+
local displayName = (n:sub(1, 1):upper() .. n:sub(2))
|
|
626
|
+
if tostring(workspace:GetAttribute(attr) or "") ~= displayName then
|
|
627
|
+
workspace:SetAttribute(attr, displayName)
|
|
628
|
+
end
|
|
629
|
+
end
|
|
630
|
+
logLevel = level
|
|
631
|
+
return
|
|
632
|
+
end
|
|
633
|
+
end
|
|
634
|
+
error("Unknown log level: " .. tostring(name))
|
|
635
|
+
end
|
|
636
|
+
local configType = type(config)
|
|
637
|
+
assert(
|
|
638
|
+
configType == "table" or configType == "string",
|
|
639
|
+
"LogConfig must return a table or a string; got " .. configType
|
|
640
|
+
)
|
|
641
|
+
if configType == "string" then
|
|
642
|
+
SetLogLevel(config)
|
|
643
|
+
else
|
|
644
|
+
if IS_STUDIO and config.Studio then
|
|
645
|
+
local studioConfigType = type(config.Studio)
|
|
646
|
+
assert(
|
|
647
|
+
studioConfigType == "table" or studioConfigType == "string",
|
|
648
|
+
"LogConfig.Studio must be a table or a string; got " .. studioConfigType
|
|
649
|
+
)
|
|
650
|
+
if studioConfigType == "string" then
|
|
651
|
+
-- Config for Studio:
|
|
652
|
+
SetLogLevel(config.Studio)
|
|
653
|
+
else
|
|
654
|
+
-- Server/Client config for Studio:
|
|
655
|
+
if IS_SERVER then
|
|
656
|
+
local studioServerLevel = config.Studio.Server
|
|
657
|
+
assert(
|
|
658
|
+
type(studioServerLevel) == "string",
|
|
659
|
+
"LogConfig.Studio.Server must be a string; got " .. type(studioServerLevel)
|
|
660
|
+
)
|
|
661
|
+
SetLogLevel(studioServerLevel)
|
|
662
|
+
else
|
|
663
|
+
local studioClientLevel = config.Studio.Client
|
|
664
|
+
assert(
|
|
665
|
+
type(studioClientLevel) == "string",
|
|
666
|
+
"LogConfig.Studio.Client must be a string; got " .. type(studioClientLevel)
|
|
667
|
+
)
|
|
668
|
+
SetLogLevel(studioClientLevel)
|
|
669
|
+
end
|
|
670
|
+
end
|
|
671
|
+
else
|
|
672
|
+
local default = nil
|
|
673
|
+
local numDefault = 0
|
|
674
|
+
local set = false
|
|
675
|
+
local setK = nil
|
|
676
|
+
for k, specialConfig in pairs(config) do
|
|
677
|
+
if k == "Studio" then
|
|
678
|
+
continue
|
|
679
|
+
end
|
|
680
|
+
if type(specialConfig) == "string" then
|
|
681
|
+
default = specialConfig
|
|
682
|
+
numDefault += 1
|
|
683
|
+
elseif type(specialConfig) == "table" then
|
|
684
|
+
-- Check if config can be used if filtered by PlaceId or GameId:
|
|
685
|
+
local canUse, fallthrough = false, false
|
|
686
|
+
if type(specialConfig.PlaceId) == "number" then
|
|
687
|
+
canUse = (specialConfig.PlaceId == game.PlaceId)
|
|
688
|
+
elseif type(specialConfig.PlaceIds) == "table" then
|
|
689
|
+
canUse = (table.find(specialConfig.PlaceIds, game.PlaceId) ~= nil)
|
|
690
|
+
elseif type(specialConfig.GameId) == "number" then
|
|
691
|
+
canUse = (specialConfig.GameId == game.GameId)
|
|
692
|
+
elseif type(specialConfig.GameIds) == "table" then
|
|
693
|
+
canUse = (table.find(specialConfig.GameIds, game.GameId) ~= nil)
|
|
694
|
+
else
|
|
695
|
+
canUse = true
|
|
696
|
+
fallthrough = true
|
|
697
|
+
end
|
|
698
|
+
if not fallthrough then
|
|
699
|
+
assert(
|
|
700
|
+
not set,
|
|
701
|
+
("More than one LogConfig mapping matched (%s and %s)"):format(setK or "", k or "")
|
|
702
|
+
)
|
|
703
|
+
end
|
|
704
|
+
if canUse then
|
|
705
|
+
if IS_SERVER then
|
|
706
|
+
local serverLevel = specialConfig.Server
|
|
707
|
+
assert(
|
|
708
|
+
type(serverLevel) == "string",
|
|
709
|
+
("LogConfig.%s.Server must be a string; got %s"):format(k, type(serverLevel))
|
|
710
|
+
)
|
|
711
|
+
SetLogLevel(serverLevel)
|
|
712
|
+
set = true
|
|
713
|
+
setK = k
|
|
714
|
+
else
|
|
715
|
+
local clientLevel = specialConfig.Client
|
|
716
|
+
assert(
|
|
717
|
+
type(clientLevel) == "string",
|
|
718
|
+
("LogConfig.%s.Client must be a string; got %s"):format(k, type(clientLevel))
|
|
719
|
+
)
|
|
720
|
+
SetLogLevel(clientLevel)
|
|
721
|
+
set = true
|
|
722
|
+
setK = k
|
|
723
|
+
end
|
|
724
|
+
end
|
|
725
|
+
else
|
|
726
|
+
warn(("LogConfig.%s must be a table or a string; got %s"):format(k, typeof(specialConfig)))
|
|
727
|
+
end
|
|
728
|
+
end
|
|
729
|
+
if numDefault > 1 then
|
|
730
|
+
warn("Ambiguous default logging level")
|
|
731
|
+
end
|
|
732
|
+
if default and not set then
|
|
733
|
+
SetLogLevel(default)
|
|
734
|
+
end
|
|
735
|
+
end
|
|
736
|
+
end
|
|
737
|
+
assert(type(logLevel) == "number", "LogLevel failed to be determined")
|
|
738
|
+
if IS_STUDIO then
|
|
739
|
+
local attr = (IS_SERVER and "LogLevel" or "LogLevelClient")
|
|
740
|
+
workspace:GetAttributeChangedSignal(attr):Connect(function()
|
|
741
|
+
SetLogLevel(workspace:GetAttribute(attr))
|
|
742
|
+
end)
|
|
743
|
+
end
|
|
744
|
+
end
|
|
745
|
+
|
|
746
|
+
return Log
|