@rbxts/zyntex-sdk 1.0.3 → 6.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/LICENSE +21 -21
- package/README.md +6 -8
- package/package.json +29 -14
- package/src/Experiments.luau +114 -0
- package/src/MainModule.luau +23 -0
- package/{Telemetry.luau → src/Telemetry.luau} +176 -176
- package/{Zyntex.luau → src/Zyntex.luau} +1631 -1509
- package/src/api.luau +237 -0
- package/src/index.d.ts +26 -0
- package/{init.luau → src/init.luau} +25 -25
- package/{types.luau → src/types.luau} +21 -21
- package/{zyntex.client.client.luau → src/zyntex.client.luau} +6 -6
- package/Loadstring/FiOne.lua +0 -1118
- package/Loadstring/Yueliang.lua +0 -4449
- package/Loadstring/init.lua +0 -57
- package/api.luau +0 -160
- package/index.d.ts +0 -89
- package/telemetry.d.ts +0 -49
package/src/api.luau
ADDED
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
--!strict
|
|
2
|
+
local HttpService: HttpService = game:GetService("HttpService")
|
|
3
|
+
local RunService = game:GetService("RunService")
|
|
4
|
+
local Random = Random.new()
|
|
5
|
+
|
|
6
|
+
--==============================--
|
|
7
|
+
-- Session
|
|
8
|
+
--==============================--
|
|
9
|
+
|
|
10
|
+
local Session = {}
|
|
11
|
+
Session.__index = Session
|
|
12
|
+
|
|
13
|
+
-- Randomized small interval to naturally jitter pushes across servers
|
|
14
|
+
local FLUSH_DELTA = Random:NextInteger(5, 8)
|
|
15
|
+
|
|
16
|
+
local BUFFER: { { [string]: any } } = {}
|
|
17
|
+
|
|
18
|
+
local _onFlush: (data: any) -> () = function(_)
|
|
19
|
+
-- no-op by default
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
--==============================--
|
|
23
|
+
-- Types
|
|
24
|
+
--==============================--
|
|
25
|
+
|
|
26
|
+
export type SessionType = {
|
|
27
|
+
rootUrl: string,
|
|
28
|
+
gameToken: string,
|
|
29
|
+
jobID: string,
|
|
30
|
+
|
|
31
|
+
_firstFlushDone: boolean,
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
local Response = {}
|
|
35
|
+
Response.__index = Response
|
|
36
|
+
|
|
37
|
+
export type ResponseType = {
|
|
38
|
+
success: boolean,
|
|
39
|
+
user_message: string,
|
|
40
|
+
data: any?,
|
|
41
|
+
statusCode: number,
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export type Session = typeof(setmetatable({} :: SessionType, Session))
|
|
45
|
+
export type Response = typeof(setmetatable({} :: ResponseType, Response))
|
|
46
|
+
|
|
47
|
+
--==============================--
|
|
48
|
+
-- Utilities
|
|
49
|
+
--==============================--
|
|
50
|
+
|
|
51
|
+
local function nowISO(): string
|
|
52
|
+
local dt = DateTime.now():ToUniversalTime()
|
|
53
|
+
return string.format(
|
|
54
|
+
"%04d-%02d-%02dT%02d:%02d:%02d.%03dZ",
|
|
55
|
+
dt.Year, dt.Month, dt.Day,
|
|
56
|
+
dt.Hour, dt.Minute, dt.Second,
|
|
57
|
+
dt.Millisecond
|
|
58
|
+
)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
local function toBatchItem(method: string, path: string, body: { [string]: any }?): { [string]: any }
|
|
62
|
+
return {
|
|
63
|
+
method = string.upper(method) .. " " .. path,
|
|
64
|
+
t = nowISO(),
|
|
65
|
+
-- keep native Lua table; server expects an object
|
|
66
|
+
payload = body or {},
|
|
67
|
+
-- lightweight client-side correlation id (handy for debugging server logs)
|
|
68
|
+
rid = HttpService:GenerateGUID(false),
|
|
69
|
+
}
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
--==============================--
|
|
73
|
+
-- Response helpers
|
|
74
|
+
--==============================--
|
|
75
|
+
|
|
76
|
+
function Response.new(success: boolean, user_message: string, statusCode: number, data: any?): Response
|
|
77
|
+
local self = {}
|
|
78
|
+
self.success = success
|
|
79
|
+
self.user_message = user_message
|
|
80
|
+
self.data = data
|
|
81
|
+
self.statusCode = statusCode
|
|
82
|
+
return setmetatable(self, Response)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
function Response.fromRaw(res: { [string]: any }, statusCode: number): Response
|
|
86
|
+
local self = {}
|
|
87
|
+
local decoded = HttpService:JSONDecode(res["Body"])
|
|
88
|
+
|
|
89
|
+
self.success = decoded.success :: boolean
|
|
90
|
+
self.user_message = decoded.user_message :: string
|
|
91
|
+
self.data = decoded.data
|
|
92
|
+
self.statusCode = statusCode
|
|
93
|
+
|
|
94
|
+
return setmetatable(self, Response)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
--==============================--
|
|
98
|
+
-- Low-level HTTP
|
|
99
|
+
--==============================--
|
|
100
|
+
|
|
101
|
+
function Session:_requestRaw(url: string, method: string, body: any?): Response
|
|
102
|
+
if not string.find(url, "http") then
|
|
103
|
+
url = self.rootUrl .. url
|
|
104
|
+
end
|
|
105
|
+
local res = HttpService:RequestAsync({
|
|
106
|
+
Url = url,
|
|
107
|
+
Method = string.upper(method),
|
|
108
|
+
Body = if body ~= nil then HttpService:JSONEncode(body) else nil,
|
|
109
|
+
Headers = {
|
|
110
|
+
["Authorization"] = `Game-Token {self.gameToken}`,
|
|
111
|
+
["Job-ID"] = self.jobID,
|
|
112
|
+
["Content-Type"] = "application/json",
|
|
113
|
+
},
|
|
114
|
+
})
|
|
115
|
+
return Response.fromRaw(res, res.StatusCode)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
--==============================--
|
|
119
|
+
-- Public API (enqueue writes, direct GET)
|
|
120
|
+
--==============================--
|
|
121
|
+
|
|
122
|
+
-- Queue a POST (batched via /roblox/push)
|
|
123
|
+
function Session.post(self: Session, path: string, body: { [string]: any }?, bypassBuffer: boolean?): string?
|
|
124
|
+
if string.find(path, "telemetry") or bypassBuffer then
|
|
125
|
+
self:_requestRaw(path, "POST", body)
|
|
126
|
+
return
|
|
127
|
+
end
|
|
128
|
+
local item = toBatchItem("POST", path, body)
|
|
129
|
+
table.insert(BUFFER, item)
|
|
130
|
+
return item.rid
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
-- Queue a DELETE (batched via /roblox/push)
|
|
134
|
+
function Session.delete(self: Session, path: string, body: { [string]: any }?, _autoError: boolean?): string
|
|
135
|
+
local item = toBatchItem("DELETE", path, body)
|
|
136
|
+
table.insert(BUFFER, item)
|
|
137
|
+
return item.rid
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
-- Direct GET (rare; kept for convenience)
|
|
141
|
+
function Session.get(self: Session, path: string): Response
|
|
142
|
+
return self:_requestRaw(self.rootUrl .. path, "GET", nil)
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
--==============================--
|
|
146
|
+
-- Flush (push)
|
|
147
|
+
--==============================--
|
|
148
|
+
|
|
149
|
+
-- Flush the buffer immediately to /roblox/push.
|
|
150
|
+
function Session.Flush(self: Session, sinceISO: string?): Response?
|
|
151
|
+
if #BUFFER == 0 then
|
|
152
|
+
return nil
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
-- drain atomically
|
|
156
|
+
local payload = BUFFER
|
|
157
|
+
BUFFER = {}
|
|
158
|
+
|
|
159
|
+
local url = self.rootUrl .. "/roblox/push"
|
|
160
|
+
if sinceISO then
|
|
161
|
+
url ..= "?since=" .. HttpService:UrlEncode(sinceISO)
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
local ok, resOrErr = pcall(function()
|
|
165
|
+
return self:_requestRaw(url, "POST", payload)
|
|
166
|
+
end)
|
|
167
|
+
|
|
168
|
+
if not ok then
|
|
169
|
+
-- put the batch back so next cycle retries
|
|
170
|
+
for _, item in payload do
|
|
171
|
+
table.insert(BUFFER, item)
|
|
172
|
+
end
|
|
173
|
+
warn(`[Zyntex]: /roblox/push flush failed, retrying later: {tostring(resOrErr)}`)
|
|
174
|
+
return nil
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
local res: Response = resOrErr :: Response
|
|
178
|
+
|
|
179
|
+
-- Invoke hook so Zyntex can react (ban/mute handling, manifest, listen_data)
|
|
180
|
+
-- This is intentionally fire-and-forget; hook can be a small, synchronous handler.
|
|
181
|
+
-- Expected shape: res.data = { results = [...], manifest?, listen_data? }
|
|
182
|
+
local okHook, hookErr = pcall(function()
|
|
183
|
+
if res.data ~= nil then
|
|
184
|
+
_onFlush(res.data)
|
|
185
|
+
end
|
|
186
|
+
end)
|
|
187
|
+
if not okHook then
|
|
188
|
+
warn(`[Zyntex]: onFlush callback errored: {tostring(hookErr)}`)
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
-- Track “first flush that contained POST /servers”
|
|
192
|
+
if not self._firstFlushDone then
|
|
193
|
+
for _, item in payload do
|
|
194
|
+
-- cheap prefix check; format is "METHOD /path"
|
|
195
|
+
if string.sub(item.method, 1, 13) == "POST /servers" then
|
|
196
|
+
self._firstFlushDone = true
|
|
197
|
+
break
|
|
198
|
+
end
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
return res
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
-- Alias; some callers prefer explicit naming
|
|
206
|
+
function Session.FlushNow(self: Session, sinceISO: string?): Response?
|
|
207
|
+
return self:Flush(sinceISO)
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
--==============================--
|
|
211
|
+
-- onFlush hook setter
|
|
212
|
+
--==============================--
|
|
213
|
+
|
|
214
|
+
-- Zyntex sets this to handle per-result errors, e.g.:
|
|
215
|
+
-- - method == "POST /players"
|
|
216
|
+
-- - success == false
|
|
217
|
+
-- - error == "403: reason" (ban) or "401: reason" (mute)
|
|
218
|
+
-- From there, Zyntex can kick/mute locally and/or print the reason.
|
|
219
|
+
function Session.onFlush(self: Session, cb: (data: any) -> ()): ()
|
|
220
|
+
_onFlush = cb or function(_) end
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
--==============================--
|
|
224
|
+
-- Constructor
|
|
225
|
+
--==============================--
|
|
226
|
+
|
|
227
|
+
function Session.new(gameToken: string, rootUrl: string?, jobId: string?): Session
|
|
228
|
+
local self: any = {}
|
|
229
|
+
self.gameToken = gameToken
|
|
230
|
+
self.rootUrl = rootUrl or "https://api.zyntex.dev"
|
|
231
|
+
self.jobID = RunService:IsStudio() and `DEV-SERVER-{HttpService:GenerateGUID(false)}` or game.JobId
|
|
232
|
+
self._firstFlushDone = false
|
|
233
|
+
|
|
234
|
+
return setmetatable(self, Session)
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
return Session
|
package/src/index.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// types/zyntex.d.ts
|
|
2
|
+
interface ZyntexConfig {}
|
|
3
|
+
interface Zyntex {
|
|
4
|
+
init(config: ZyntexConfig): void;
|
|
5
|
+
// Add other Zyntex instance methods here
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
interface ZyntexModule {
|
|
9
|
+
/**
|
|
10
|
+
* Creates a new Zyntex instance using the provided game token.
|
|
11
|
+
*/
|
|
12
|
+
(gameToken: string): Zyntex;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* The current version string of the Zyntex module.
|
|
16
|
+
*/
|
|
17
|
+
readonly version: string;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Used by the upgrader plugin to detect Zyntex.
|
|
21
|
+
*/
|
|
22
|
+
readonly isZyntex: true;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
declare const ZyntexModule: ZyntexModule;
|
|
26
|
+
export default ZyntexModule;
|
|
@@ -1,26 +1,26 @@
|
|
|
1
|
-
local Zyntex = require(script:FindFirstChild("Zyntex"))
|
|
2
|
-
local Session = require(script:FindFirstChild("api"))
|
|
3
|
-
|
|
4
|
-
return {
|
|
5
|
-
default = setmetatable({}, {
|
|
6
|
-
--[[
|
|
7
|
-
Gets the current version of this module.
|
|
8
|
-
]]
|
|
9
|
-
__index = function(self, index)
|
|
10
|
-
if string.lower(index) == "version" then
|
|
11
|
-
return Zyntex.VERSION
|
|
12
|
-
end
|
|
13
|
-
if string.lower(index) == "isZyntex" then --// Important for the Zyntex upgrader plugin. When it searches for the module, make sure it has this.
|
|
14
|
-
return true
|
|
15
|
-
end
|
|
16
|
-
end,
|
|
17
|
-
--[[
|
|
18
|
-
Returns a new Zyntex object.
|
|
19
|
-
https://docs.zyntex.dev
|
|
20
|
-
]]
|
|
21
|
-
__call = function(self, gameToken: string): Zyntex.Zyntex
|
|
22
|
-
return Zyntex.new(gameToken)
|
|
23
|
-
end,
|
|
24
|
-
})
|
|
25
|
-
|
|
1
|
+
local Zyntex = require(script:FindFirstChild("Zyntex"))
|
|
2
|
+
local Session = require(script:FindFirstChild("api"))
|
|
3
|
+
|
|
4
|
+
return {
|
|
5
|
+
default = setmetatable({}, {
|
|
6
|
+
--[[
|
|
7
|
+
Gets the current version of this module.
|
|
8
|
+
]]
|
|
9
|
+
__index = function(self, index)
|
|
10
|
+
if string.lower(index) == "version" then
|
|
11
|
+
return Zyntex.VERSION
|
|
12
|
+
end
|
|
13
|
+
if string.lower(index) == "isZyntex" then --// Important for the Zyntex upgrader plugin. When it searches for the module, make sure it has this.
|
|
14
|
+
return true
|
|
15
|
+
end
|
|
16
|
+
end,
|
|
17
|
+
--[[
|
|
18
|
+
Returns a new Zyntex object.
|
|
19
|
+
https://docs.zyntex.dev
|
|
20
|
+
]]
|
|
21
|
+
__call = function(self, gameToken: string): Zyntex.Zyntex
|
|
22
|
+
return Zyntex.new(gameToken)
|
|
23
|
+
end,
|
|
24
|
+
})
|
|
25
|
+
|
|
26
26
|
}
|
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
--[[
|
|
2
|
-
The Zyntex configuration type.
|
|
3
|
-
]]
|
|
4
|
-
export type Config = {
|
|
5
|
-
--[[
|
|
6
|
-
Wether or not to list to and execute requests made by the dashboard server.
|
|
7
|
-
Admins require the servers.rce permission to remotely execute code.
|
|
8
|
-
Defaults to "true"
|
|
9
|
-
]]
|
|
10
|
-
enableRCE: boolean?;
|
|
11
|
-
--[[
|
|
12
|
-
Whether or not to display debug logging
|
|
13
|
-
]]
|
|
14
|
-
debug: boolean?;
|
|
15
|
-
--[[
|
|
16
|
-
Wether or not to simulate high-requests
|
|
17
|
-
]]
|
|
18
|
-
simulate: boolean?;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
return true;
|
|
1
|
+
--[[
|
|
2
|
+
The Zyntex configuration type.
|
|
3
|
+
]]
|
|
4
|
+
export type Config = {
|
|
5
|
+
--[[
|
|
6
|
+
Wether or not to list to and execute requests made by the dashboard server.
|
|
7
|
+
Admins require the servers.rce permission to remotely execute code.
|
|
8
|
+
Defaults to "true"
|
|
9
|
+
]]
|
|
10
|
+
enableRCE: boolean?;
|
|
11
|
+
--[[
|
|
12
|
+
Whether or not to display debug logging
|
|
13
|
+
]]
|
|
14
|
+
debug: boolean?;
|
|
15
|
+
--[[
|
|
16
|
+
Wether or not to simulate high-requests
|
|
17
|
+
]]
|
|
18
|
+
simulate: boolean?;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return true;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
local SystemChatEvent: RemoteEvent = game:GetService("ReplicatedStorage"):WaitForChild("zyntex.events"):WaitForChild("SystemChat") :: RemoteEvent
|
|
2
|
-
local Channel = game.TextChatService:WaitForChild("TextChannels"):WaitForChild("RBXGeneral")
|
|
3
|
-
|
|
4
|
-
SystemChatEvent.OnClientEvent:Connect(function(msg: string)
|
|
5
|
-
Channel:DisplaySystemMessage(msg)
|
|
6
|
-
end)
|
|
1
|
+
local SystemChatEvent: RemoteEvent = game:GetService("ReplicatedStorage"):WaitForChild("zyntex.events"):WaitForChild("SystemChat") :: RemoteEvent
|
|
2
|
+
local Channel = game.TextChatService:WaitForChild("TextChannels"):WaitForChild("RBXGeneral")
|
|
3
|
+
|
|
4
|
+
SystemChatEvent.OnClientEvent:Connect(function(msg: string)
|
|
5
|
+
Channel:DisplaySystemMessage(msg)
|
|
6
|
+
end)
|