mirror of
				https://github.com/imezx/Warp.git
				synced 2025-10-24 23:39:18 +00:00 
			
		
		
		
	Compare commits
	
		
			22 commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 0b1304f4c5 | ||
|  | d8526c7e25 | ||
|  | d065bc2e50 | ||
|  | 925df47d4c | ||
|  | f3b674b377 | ||
|  | 677d3fa675 | ||
|  | 91862a65fe | ||
|  | 8ba9540550 | ||
|  | 1d67954ef9 | ||
|  | 43c4a1594f | ||
|  | 22996c9357 | ||
|  | a377788f22 | ||
|  | 3354324c5b | ||
|  | 20b97eeb54 | ||
|  | dbed984eea | ||
|  | 064075fbd9 | ||
|  | 77de85b6b8 | ||
|  | 10de54608a | ||
|  | 5b2e36b7bb | ||
|  | eba9f79655 | ||
|  | 7309840005 | ||
|  | aa693aee4f | 
					 29 changed files with 697 additions and 468 deletions
				
			
		
							
								
								
									
										10
									
								
								.github/workflows/deploy.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										10
									
								
								.github/workflows/deploy.yml
									
									
									
									
										vendored
									
									
								
							|  | @ -18,16 +18,16 @@ jobs: | ||||||
|       url: ${{ steps.deployment.outputs.page_url }} |       url: ${{ steps.deployment.outputs.page_url }} | ||||||
|     steps: |     steps: | ||||||
|       - name: Checkout |       - name: Checkout | ||||||
|         uses: actions/checkout@v3 |         uses: actions/checkout@v4 | ||||||
|         with: |         with: | ||||||
|           fetch-depth: 0 |           fetch-depth: 0 | ||||||
|       - name: Setup Node |       - name: Setup Node | ||||||
|         uses: actions/setup-node@v3 |         uses: actions/setup-node@v4 | ||||||
|         with: |         with: | ||||||
|           node-version: 20 |           node-version: 20 | ||||||
|           cache: npm |           cache: npm | ||||||
|       - name: Setup Pages |       - name: Setup Pages | ||||||
|         uses: actions/configure-pages@v3 |         uses: actions/configure-pages@v4 | ||||||
|       - name: Install dependencies |       - name: Install dependencies | ||||||
|         run: npm ci |         run: npm ci | ||||||
|       - name: Build |       - name: Build | ||||||
|  | @ -35,9 +35,9 @@ jobs: | ||||||
|           npm run docs:build |           npm run docs:build | ||||||
|           touch docs/.vitepress/dist |           touch docs/.vitepress/dist | ||||||
|       - name: Upload artifact |       - name: Upload artifact | ||||||
|         uses: actions/upload-pages-artifact@v2 |         uses: actions/upload-pages-artifact@v3 | ||||||
|         with: |         with: | ||||||
|           path: docs/.vitepress/dist |           path: docs/.vitepress/dist | ||||||
|       - name: Deploy |       - name: Deploy | ||||||
|         id: deployment |         id: deployment | ||||||
|         uses: actions/deploy-pages@v2 |         uses: actions/deploy-pages@v4 | ||||||
|  |  | ||||||
							
								
								
									
										
											BIN
										
									
								
								Warp.rbxm
									
									
									
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								Warp.rbxm
									
									
									
									
									
								
							
										
											Binary file not shown.
										
									
								
							|  | @ -39,7 +39,6 @@ function side() { | ||||||
|             text: 'Utilities', |             text: 'Utilities', | ||||||
|             items: [ |             items: [ | ||||||
|               { text: 'Signal', link: '/api/1.0/signal' }, |               { text: 'Signal', link: '/api/1.0/signal' }, | ||||||
|               { text: 'Buffer', link: '/api/1.0/buffer' }, |  | ||||||
|             ] |             ] | ||||||
|           }, |           }, | ||||||
|         ] |         ] | ||||||
|  |  | ||||||
|  | @ -1,3 +0,0 @@ | ||||||
| # Buffer <Badge type="tip" text="utilities" /> |  | ||||||
| 
 |  | ||||||
| A Additional buffer. |  | ||||||
|  | @ -13,8 +13,10 @@ When creating a event on Server, you can add second argument (optional) as table | ||||||
| -- Server | -- Server | ||||||
| -- Let's make the event have ratelimit with max 50 entrance for 2 seconds. | -- Let's make the event have ratelimit with max 50 entrance for 2 seconds. | ||||||
| local Remote = Warp.Server("Remote1", { | local Remote = Warp.Server("Remote1", { | ||||||
|  | 	rateLimit = { | ||||||
| 		maxEntrance = 50, -- maximum 50 fires. | 		maxEntrance = 50, -- maximum 50 fires. | ||||||
| 		interval = 2, -- 2 seconds | 		interval = 2, -- 2 seconds | ||||||
|  | 	} | ||||||
| }) | }) | ||||||
| -- Now the Event RateLimit is configured, and ready to use. | -- Now the Event RateLimit is configured, and ready to use. | ||||||
| -- No need anything to adds on client side. | -- No need anything to adds on client side. | ||||||
|  |  | ||||||
|  | @ -36,12 +36,16 @@ Create new Warp events with array. | ||||||
| ```lua [Example] | ```lua [Example] | ||||||
| local Events = Warp.fromServerArray({ | local Events = Warp.fromServerArray({ | ||||||
| 	["Remote1"] = { | 	["Remote1"] = { | ||||||
|  | 		rateLimit = { | ||||||
| 			maxEntrance: 50, | 			maxEntrance: 50, | ||||||
| 			interval: 1, | 			interval: 1, | ||||||
|  | 		} | ||||||
| 	}, -- with rateLimit configuration | 	}, -- with rateLimit configuration | ||||||
| 	"Remote2", -- without rateLimit configuration | 	"Remote2", -- without rateLimit configuration | ||||||
| 	["Remote3"] = { | 	["Remote3"] = { | ||||||
|  | 		rateLimit = { | ||||||
| 			maxEntrance: 10, | 			maxEntrance: 10, | ||||||
|  | 		} | ||||||
| 	}, -- with rateLimit configuration | 	}, -- with rateLimit configuration | ||||||
| }) | }) | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -102,7 +102,23 @@ Signal1:DisconnectAll() | ||||||
| 
 | 
 | ||||||
| ## `:Fire` | ## `:Fire` | ||||||
| 
 | 
 | ||||||
| Fire the signal. | Fire the signal (Immediate) | ||||||
|  | 
 | ||||||
|  | ::: code-group | ||||||
|  | ```lua [Variable] | ||||||
|  | ( | ||||||
|  | 	...: any | ||||||
|  | ) | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | ```lua [Example] | ||||||
|  | Signal1:Fire("Hello World!") | ||||||
|  | ``` | ||||||
|  | ::: | ||||||
|  | 
 | ||||||
|  | ## `:DeferFire` | ||||||
|  | 
 | ||||||
|  | Fire the signal (Deferred) | ||||||
| 
 | 
 | ||||||
| ::: code-group | ::: code-group | ||||||
| ```lua [Variable] | ```lua [Variable] | ||||||
|  | @ -122,7 +138,7 @@ This uses `pcall`, which means it never error (safe-mode, sacrificed debugging), | ||||||
| 
 | 
 | ||||||
| ## `:FireTo` | ## `:FireTo` | ||||||
| 
 | 
 | ||||||
| Fire to other signal, this also use `:Fire`. | Fire to other signal, this uses `:Fire`. | ||||||
| 
 | 
 | ||||||
| ::: code-group | ::: code-group | ||||||
| ```lua [Variable] | ```lua [Variable] | ||||||
|  |  | ||||||
|  | @ -68,3 +68,4 @@ Pong:Destroy() | ||||||
| 
 | 
 | ||||||
| -- Yay Done! | -- Yay Done! | ||||||
| ``` | ``` | ||||||
|  | ::: | ||||||
|  | @ -8,7 +8,7 @@ | ||||||
| ::: code-group | ::: code-group | ||||||
| ```toml [wally.toml] | ```toml [wally.toml] | ||||||
| [dependencies] | [dependencies] | ||||||
| warp = "imezx/warp@1.0.5" | warp = "imezx/warp@1.0.13" | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| 3. Run `wally install` in command. | 3. Run `wally install` in command. | ||||||
|  |  | ||||||
|  | @ -1,34 +0,0 @@ | ||||||
| --!strict |  | ||||||
| --!native |  | ||||||
| --!optimize 2 |  | ||||||
| local Logger = {} |  | ||||||
| local Logs: { |  | ||||||
| 	[string]: { |  | ||||||
| 		[string]: string |  | ||||||
| 	} |  | ||||||
| } = {} |  | ||||||
| local logging: { |  | ||||||
| 	[string]: boolean |  | ||||||
| } = {} |  | ||||||
| 
 |  | ||||||
| local now = tick() |  | ||||||
| 
 |  | ||||||
| function Logger.write(Identifier: string, text: string, log: boolean?) |  | ||||||
| 	if not Logs[Identifier] then |  | ||||||
| 		Logs[Identifier] = {} |  | ||||||
| 	end |  | ||||||
| 	if log ~= nil then |  | ||||||
| 		logging[Identifier] = log |  | ||||||
| 	end |  | ||||||
| 	now = tick() |  | ||||||
| 	Logs[Identifier][tostring(now)] = text |  | ||||||
| 	if logging[Identifier] then |  | ||||||
| 		print(`[{now}] ->`, text) |  | ||||||
| 	end |  | ||||||
| end |  | ||||||
| 
 |  | ||||||
| function Logger.read(Identifier: string) |  | ||||||
| 	return Logs[Identifier] |  | ||||||
| end |  | ||||||
| 
 |  | ||||||
| return Logger |  | ||||||
|  | @ -12,17 +12,14 @@ local Spawn = require(Util.Spawn) | ||||||
| local Key = require(Util.Key) | local Key = require(Util.Key) | ||||||
| local RateLimit = require(Util.RateLimit) | local RateLimit = require(Util.RateLimit) | ||||||
| local Buffer = require(Util.Buffer) | local Buffer = require(Util.Buffer) | ||||||
| local Logger = require(script.Logger) |  | ||||||
| 
 | 
 | ||||||
| local clientRatelimit: Type.StoredRatelimit = {} | local clientRatelimit: Type.StoredRatelimit = {} | ||||||
| local clientQueue: Type.QueueMap = {} | local clientQueue: Type.QueueMap = {} | ||||||
| local unreliableClientQueue: Type.QueueMap = {} | local unreliableClientQueue: Type.QueueMap = {} | ||||||
| local clientCallback: Type.CallbackMap = {} | local clientCallback: Type.CallbackMap = {} | ||||||
| local clientRequestQueue: Type.QueueMap = {} | local clientRequestQueue: Type.QueueMap = {} | ||||||
|  | local registeredIdentifier: { [string]: boolean } = {} | ||||||
| 
 | 
 | ||||||
| local queueIn: { |  | ||||||
| 	[string]: {any} |  | ||||||
| } = {} |  | ||||||
| local queueInRequest: { | local queueInRequest: { | ||||||
| 	[number]: { | 	[number]: { | ||||||
| 		[string]: { | 		[string]: { | ||||||
|  | @ -37,14 +34,6 @@ local queueOutRequest: { | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| } = {} | } = {} | ||||||
| local incoming_cache: { |  | ||||||
| 	[string]: { |  | ||||||
| 		any |  | ||||||
| 	} |  | ||||||
| } = {} |  | ||||||
| local logger: { |  | ||||||
| 	[string]: boolean |  | ||||||
| } = {} |  | ||||||
| 
 | 
 | ||||||
| queueInRequest[1] = {} | queueInRequest[1] = {} | ||||||
| queueInRequest[2] = {} | queueInRequest[2] = {} | ||||||
|  | @ -86,10 +75,9 @@ function ClientProcess.insertRequest(Identifier: string, timeout: number, ...: a | ||||||
| end | end | ||||||
| 
 | 
 | ||||||
| function ClientProcess.add(Identifier: any, originId: string, conf: Type.ClientConf) | function ClientProcess.add(Identifier: any, originId: string, conf: Type.ClientConf) | ||||||
| 	if not clientQueue[Identifier] then | 	if not registeredIdentifier[Identifier] then | ||||||
| 		if conf.logging then | 		registeredIdentifier[Identifier] = true | ||||||
| 			ClientProcess.logger(Identifier, conf.logging.store, conf.logging.opt) | 
 | ||||||
| 		end |  | ||||||
| 		if not clientRatelimit[Identifier] then | 		if not clientRatelimit[Identifier] then | ||||||
| 			clientRatelimit[Identifier] = RateLimit.create(originId) | 			clientRatelimit[Identifier] = RateLimit.create(originId) | ||||||
| 		end | 		end | ||||||
|  | @ -118,59 +106,69 @@ function ClientProcess.add(Identifier: any, originId: string, conf: Type.ClientC | ||||||
| 		if not queueInRequest[2][Identifier] then | 		if not queueInRequest[2][Identifier] then | ||||||
| 			queueInRequest[2][Identifier] = {} | 			queueInRequest[2][Identifier] = {} | ||||||
| 		end | 		end | ||||||
| 		if not queueIn[Identifier] then |  | ||||||
| 			queueIn[Identifier] = {} |  | ||||||
| 		end |  | ||||||
| 	end | 	end | ||||||
| end | end | ||||||
| 
 | 
 | ||||||
| function ClientProcess.logger(Identifier: string, store: boolean, log: boolean) | function ClientProcess.remove(Identifier: string) | ||||||
| 	logger[Identifier] = store | 	if not registeredIdentifier[Identifier] then return end | ||||||
| 	Logger.write(Identifier, `state: change -> {log == true and "enabled" or "disabled"} logger.`, log) | 	registeredIdentifier[Identifier] = nil | ||||||
| end | 	clientQueue[Identifier] = nil | ||||||
| 
 | 	unreliableClientQueue[Identifier] = nil | ||||||
| function ClientProcess.getlogs(Identifier: string) | 	clientRequestQueue[Identifier] = nil | ||||||
| 	return Logger.read(Identifier) | 	clientCallback[Identifier] = nil | ||||||
|  | 	clientRatelimit[Identifier] = nil | ||||||
|  | 	queueOutRequest[1][Identifier] = nil | ||||||
|  | 	queueOutRequest[2][Identifier] = nil | ||||||
|  | 	queueInRequest[1][Identifier] = nil | ||||||
|  | 	queueInRequest[2][Identifier] = nil | ||||||
| end | end | ||||||
| 
 | 
 | ||||||
| function ClientProcess.addCallback(Identifier: string, key: string, callback) | function ClientProcess.addCallback(Identifier: string, key: string, callback) | ||||||
| 	clientCallback[Identifier][key] = callback | 	clientCallback[Identifier][key] = callback | ||||||
| 	if logger[Identifier] then |  | ||||||
| 		task.defer(Logger.write, Identifier, `state: change -> new callback added.`) |  | ||||||
| 	end |  | ||||||
| end | end | ||||||
| 
 | 
 | ||||||
| function ClientProcess.removeCallback(Identifier: string, key: string) | function ClientProcess.removeCallback(Identifier: string, key: string) | ||||||
| 	clientCallback[Identifier][key] = nil | 	clientCallback[Identifier][key] = nil | ||||||
| 	if logger[Identifier] then |  | ||||||
| 		task.defer(Logger.write, Identifier, `state: change -> removed a callback.`) |  | ||||||
| 	end |  | ||||||
| end | end | ||||||
| 
 | 
 | ||||||
| function ClientProcess.start() | function ClientProcess.start() | ||||||
| 	debug.setmemorycategory("Warp") | 	debug.setmemorycategory("Warp") | ||||||
| 	RunService.PostSimulation:Connect(function() | 	RunService.PostSimulation:Connect(function() | ||||||
|  | 		-- Unreliable | ||||||
| 		for Identifier: string, data: any in unreliableClientQueue do | 		for Identifier: string, data: any in unreliableClientQueue do | ||||||
| 			if #data == 0 then continue end | 			if #data == 0 then continue end | ||||||
| 			if clientRatelimit[Identifier](#data) then | 			if clientRatelimit[Identifier](#data) then | ||||||
| 				UnreliableEvent:FireServer(Buffer.revert(Identifier), data) | 				for _, unpacked in data do | ||||||
| 				if logger[Identifier] then | 					UnreliableEvent:FireServer(Buffer.revert(Identifier), Buffer.write(unpacked)) | ||||||
| 					task.defer(Logger.write, Identifier, `state: out -> unreliable -> {#data} data.`) |  | ||||||
| 				end | 				end | ||||||
| 			end | 			end | ||||||
| 			unreliableClientQueue[Identifier] = nil | 			unreliableClientQueue[Identifier] = nil | ||||||
| 		end | 		end | ||||||
|  | 		-- Reliable | ||||||
| 		for Identifier: string, data: any in clientQueue do | 		for Identifier: string, data: any in clientQueue do | ||||||
| 			local callback = clientCallback[Identifier] or nil |  | ||||||
| 			if #data > 0 then | 			if #data > 0 then | ||||||
| 				if clientRatelimit[Identifier](#data) then | 				if clientRatelimit[Identifier](#data) then | ||||||
| 					ReliableEvent:FireServer(Buffer.revert(Identifier), data) | 					for _, unpacked in data do | ||||||
| 					if logger[Identifier] then | 						ReliableEvent:FireServer(Buffer.revert(Identifier), Buffer.write(unpacked)) | ||||||
| 						task.defer(Logger.write, Identifier, `state: out -> reliable -> {#data} data.`) |  | ||||||
| 					end | 					end | ||||||
| 				end | 				end | ||||||
| 				clientQueue[Identifier] = nil | 				clientQueue[Identifier] = nil | ||||||
| 			end | 			end | ||||||
|  | 		end | ||||||
|  | 		-- Sent new invokes | ||||||
|  | 		for Identifier: string, requestsData in queueOutRequest[1] do | ||||||
|  | 			if #requestsData == 0 then continue end | ||||||
|  | 			RequestEvent:FireServer(Buffer.revert(Identifier), "\1", requestsData) | ||||||
|  | 			queueOutRequest[1][Identifier] = nil | ||||||
|  | 		end | ||||||
|  | 		-- Sent returning invokes | ||||||
|  | 		for Identifier: string, toReturnDatas in queueOutRequest[2] do | ||||||
|  | 			if #toReturnDatas == 0 then continue end | ||||||
|  | 			RequestEvent:FireServer(Buffer.revert(Identifier), "\0", toReturnDatas) | ||||||
|  | 			queueOutRequest[2][Identifier] = nil | ||||||
|  | 		end | ||||||
|  | 
 | ||||||
|  | 		for Identifier: string in registeredIdentifier do | ||||||
| 			if clientRequestQueue[Identifier] then | 			if clientRequestQueue[Identifier] then | ||||||
| 				for _, requestData in clientRequestQueue[Identifier] do | 				for _, requestData in clientRequestQueue[Identifier] do | ||||||
| 					if not requestData[3] then continue end | 					if not requestData[3] then continue end | ||||||
|  | @ -178,51 +176,39 @@ function ClientProcess.start() | ||||||
| 						queueOutRequest[1][Identifier] = {} | 						queueOutRequest[1][Identifier] = {} | ||||||
| 					end | 					end | ||||||
| 					table.insert(queueOutRequest[1][Identifier], { requestData[1], requestData[3] }) | 					table.insert(queueOutRequest[1][Identifier], { requestData[1], requestData[3] }) | ||||||
| 				end | 					requestData[3] = nil | ||||||
| 				clientRequestQueue[Identifier] = nil |  | ||||||
| 			end |  | ||||||
| 			if callback then |  | ||||||
| 				if incoming_cache[Identifier] then |  | ||||||
| 					for _, packet in incoming_cache[Identifier] do |  | ||||||
| 						if #packet == 0 then continue end |  | ||||||
| 						for _, fn: any in callback do |  | ||||||
| 							for i=1,#packet do |  | ||||||
| 								Spawn(fn, table.unpack(packet[i] or {})) |  | ||||||
| 				end | 				end | ||||||
| 			end | 			end | ||||||
| 					end | 
 | ||||||
| 					incoming_cache[Identifier] = nil | 			-- Unreliable & Reliable | ||||||
| 				end | 			local callback = clientCallback[Identifier] or nil | ||||||
| 				if queueIn[Identifier] then | 			if not callback then continue end | ||||||
| 					for _, packedDatas: any in queueIn[Identifier] do | 
 | ||||||
| 						if #packedDatas == 0 then continue end | 			-- Return Invoke | ||||||
| 						for _, fn: any in callback do |  | ||||||
| 							for i=1,#packedDatas do |  | ||||||
| 								Spawn(fn, table.unpack(packedDatas[i] or {})) |  | ||||||
| 							end |  | ||||||
| 						end |  | ||||||
| 					end |  | ||||||
| 					queueIn[Identifier] = nil |  | ||||||
| 				end |  | ||||||
| 			if queueInRequest[1][Identifier] then | 			if queueInRequest[1][Identifier] then | ||||||
| 				for _, packetDatas: any in queueInRequest[1][Identifier] do | 				for _, packetDatas: any in queueInRequest[1][Identifier] do | ||||||
| 					if #packetDatas == 0 then continue end | 					if #packetDatas == 0 then continue end | ||||||
| 					for _, fn: any in callback do | 					for _, fn: any in callback do | ||||||
| 						for i=1,#packetDatas do | 						for i=1,#packetDatas do | ||||||
| 								local packetData = packetDatas[i] | 							if not packetDatas[i] then continue end | ||||||
| 								if not packetData then continue end | 							local packetData1 = packetDatas[i][1] | ||||||
|  | 							local packetData2 = packetDatas[i][2] | ||||||
| 							Spawn(function() | 							Spawn(function() | ||||||
| 									local requestReturn = { fn(table.unpack(packetData[2])) } | 								local requestReturn = { fn(table.unpack(packetData2)) } | ||||||
| 								if not queueOutRequest[2][Identifier] then | 								if not queueOutRequest[2][Identifier] then | ||||||
| 									queueOutRequest[2][Identifier] = {} | 									queueOutRequest[2][Identifier] = {} | ||||||
| 								end | 								end | ||||||
| 									table.insert(queueOutRequest[2][Identifier], { packetData[1],  requestReturn }) | 								table.insert(queueOutRequest[2][Identifier], { packetData1,  requestReturn }) | ||||||
|  | 								packetData1 = nil | ||||||
|  | 								packetData2 = nil | ||||||
| 							end) | 							end) | ||||||
| 						end | 						end | ||||||
| 					end | 					end | ||||||
| 				end | 				end | ||||||
| 				queueInRequest[1][Identifier] = nil | 				queueInRequest[1][Identifier] = nil | ||||||
| 			end | 			end | ||||||
|  | 
 | ||||||
|  | 			-- Call to Invoke | ||||||
| 			if queueInRequest[2][Identifier] then | 			if queueInRequest[2][Identifier] then | ||||||
| 				if clientRequestQueue[Identifier] then | 				if clientRequestQueue[Identifier] then | ||||||
| 					for _, packetDatas: any in queueInRequest[2][Identifier] do | 					for _, packetDatas: any in queueInRequest[2][Identifier] do | ||||||
|  | @ -243,43 +229,17 @@ function ClientProcess.start() | ||||||
| 				queueInRequest[2][Identifier] = nil | 				queueInRequest[2][Identifier] = nil | ||||||
| 			end | 			end | ||||||
| 		end | 		end | ||||||
| 		end |  | ||||||
| 		for Identifier: string, requestsData in queueOutRequest[1] do |  | ||||||
| 			if #requestsData == 0 then continue end |  | ||||||
| 			RequestEvent:FireServer(Buffer.revert(Identifier), "\1", requestsData) |  | ||||||
| 			if logger[Identifier] then |  | ||||||
| 				task.defer(Logger.write, Identifier, `state: out -> request -> {#requestsData} data.`) |  | ||||||
| 			end |  | ||||||
| 			queueOutRequest[1][Identifier] = nil |  | ||||||
| 		end |  | ||||||
| 		for Identifier: string, toReturnDatas in queueOutRequest[2] do |  | ||||||
| 			if #toReturnDatas == 0 then continue end |  | ||||||
| 			RequestEvent:FireServer(Buffer.revert(Identifier), "\0", toReturnDatas) |  | ||||||
| 			if logger[Identifier] then |  | ||||||
| 				task.defer(Logger.write, Identifier, `state: out -> return request -> {#toReturnDatas} data.`) |  | ||||||
| 			end |  | ||||||
| 			queueOutRequest[2][Identifier] = nil |  | ||||||
| 		end |  | ||||||
| 	end) | 	end) | ||||||
| 	local function onClientNetworkReceive(Identifier: any, data: any) | 	local function onClientNetworkReceive(Identifier: buffer | string, data: buffer, ref: { any }?) | ||||||
| 		if not Identifier or not data then return end | 		if not Identifier or typeof(Identifier) ~= "buffer" or not data or typeof(data) ~= "buffer" then return end | ||||||
| 		Identifier = Buffer.convert(Identifier) | 		Identifier = Buffer.convert(Identifier) | ||||||
| 		if not queueIn[Identifier] then | 		if not registeredIdentifier[Identifier :: string] then return end | ||||||
| 			queueIn[Identifier] = {} | 		local read = Buffer.read(data, ref) | ||||||
| 		end | 		if not read then return end | ||||||
| 		if not clientCallback[Identifier] then | 		local callback = clientCallback[Identifier :: string] | ||||||
| 			if not incoming_cache[Identifier] then | 		if not callback then return end | ||||||
| 				incoming_cache[Identifier] = {} | 		for _, fn: any in callback do | ||||||
| 			end | 			Spawn(fn, table.unpack(read)) | ||||||
| 			table.insert(incoming_cache[Identifier], data) |  | ||||||
| 			if logger[Identifier] then |  | ||||||
| 				task.defer(Logger.write, Identifier, `state: cache -> net -> {#data} data.`) |  | ||||||
| 			end |  | ||||||
| 			return |  | ||||||
| 		end |  | ||||||
| 		table.insert(queueIn[Identifier], data) |  | ||||||
| 		if logger[Identifier] then |  | ||||||
| 			task.defer(Logger.write, Identifier, `state: in -> net -> {#data} data.`) |  | ||||||
| 		end | 		end | ||||||
| 	end | 	end | ||||||
| 	ReliableEvent.OnClientEvent:Connect(onClientNetworkReceive) | 	ReliableEvent.OnClientEvent:Connect(onClientNetworkReceive) | ||||||
|  | @ -298,9 +258,6 @@ function ClientProcess.start() | ||||||
| 			end | 			end | ||||||
| 			table.insert(queueInRequest[2][Identifier], data) | 			table.insert(queueInRequest[2][Identifier], data) | ||||||
| 		end | 		end | ||||||
| 		if logger[Identifier] then |  | ||||||
| 			task.defer(Logger.write, Identifier, `state: in -> request -> {#data} data.`) |  | ||||||
| 		end |  | ||||||
| 	end) | 	end) | ||||||
| end | end | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -18,23 +18,18 @@ function Client.new(Identifier: string, conf: Type.ClientConf?) | ||||||
| 	local self = setmetatable({}, Client) | 	local self = setmetatable({}, Client) | ||||||
| 
 | 
 | ||||||
| 	self._buffer = Buffer.new() | 	self._buffer = Buffer.new() | ||||||
| 	self._buffer:wu8(Serdes(Identifier, conf and conf.yieldWait)) | 	self._buffer:wu8(Serdes.increment(Identifier, conf and conf.yieldWait)) | ||||||
| 	self.id = Buffer.convert(self._buffer:build()) | 	self.id = Buffer.convert(self._buffer:build()) | ||||||
| 	self.fn = {} | 	self.fn = {} | ||||||
| 	self._conf = table.freeze(conf or {}) | 	self._conf = table.freeze(conf or {}) | ||||||
| 	self.IsConnected = false | 	self.IsConnected = false | ||||||
| 
 | 
 | ||||||
| 	ClientProcess.add(self.id, Identifier, conf or { yieldWait = 10, logging = { store = false, opt = false } }) | 	ClientProcess.add(self.id, Identifier, conf or { yieldWait = 10 }) | ||||||
| 	self._buffer:remove() | 	self._buffer:remove() | ||||||
| 
 | 
 | ||||||
| 	return self | 	return self | ||||||
| end | end | ||||||
| 
 | 
 | ||||||
| function Client:logs() |  | ||||||
| 	Assert(self._conf.logging, "[Client]: Event is not configured with logging.") |  | ||||||
| 	return ClientProcess.getlogs(self.id) |  | ||||||
| end |  | ||||||
| 
 |  | ||||||
| function Client:Fire(reliable: boolean,...: any) | function Client:Fire(reliable: boolean,...: any) | ||||||
| 	ClientProcess.insertQueue(self.id, reliable, ...) | 	ClientProcess.insertQueue(self.id, reliable, ...) | ||||||
| end | end | ||||||
|  | @ -55,7 +50,7 @@ function Client:Once(callback: (args: any) -> ()): string | ||||||
| 	local key = tostring(Key()) | 	local key = tostring(Key()) | ||||||
| 	table.insert(self.fn, key) | 	table.insert(self.fn, key) | ||||||
| 	self.IsConnected = #self.fn > 0 | 	self.IsConnected = #self.fn > 0 | ||||||
| 	ClientProcess.addCallback(self.id, key, function(...) | 	ClientProcess.addCallback(self.id, key, function(...: any?) | ||||||
| 		self:Disconnect(key) | 		self:Disconnect(key) | ||||||
| 		task.spawn(callback, ...) | 		task.spawn(callback, ...) | ||||||
| 	end) | 	end) | ||||||
|  | @ -76,16 +71,19 @@ function Client:DisconnectAll() | ||||||
| 	end | 	end | ||||||
| end | end | ||||||
| 
 | 
 | ||||||
| function Client:Disconnect(key: string): boolean | function Client:Disconnect(key: string) | ||||||
| 	Assert(typeof(key) == "string", "Key must be a string type.") | 	Assert(typeof(key) == "string", "Key must be a string type.") | ||||||
| 	ClientProcess.removeCallback(self.id, key) | 	ClientProcess.removeCallback(self.id, key) | ||||||
| 	table.remove(self.fn, table.find(self.fn, key)) | 	table.remove(self.fn, table.find(self.fn, key)) | ||||||
| 	self.IsConnected = #self.fn > 0 | 	self.IsConnected = #self.fn > 0 | ||||||
| 	return table.find(self.fn, key) == nil |  | ||||||
| end | end | ||||||
| 
 | 
 | ||||||
| function Client:Destroy() | function Client:Destroy() | ||||||
| 	self:DisconnectAll() | 	self:DisconnectAll() | ||||||
|  | 	self._buffer:remove() | ||||||
|  | 	ClientProcess.remove(self.id) | ||||||
|  | 	Serdes.decrement() | ||||||
|  | 	table.clear(self) | ||||||
| 	setmetatable(self, nil) | 	setmetatable(self, nil) | ||||||
| end | end | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,5 +1,4 @@ | ||||||
| --!strict | --!strict | ||||||
| --!native |  | ||||||
| --!optimize 2 | --!optimize 2 | ||||||
| local RunService = game:GetService("RunService") | local RunService = game:GetService("RunService") | ||||||
| local Type = require(script.Parent.Type) | local Type = require(script.Parent.Type) | ||||||
|  |  | ||||||
|  | @ -18,7 +18,7 @@ function Server.new(Identifier: string, conf: Type.ServerConf?) | ||||||
| 	local self = setmetatable({}, Server) | 	local self = setmetatable({}, Server) | ||||||
| 
 | 
 | ||||||
| 	self._buffer = Buffer.new() | 	self._buffer = Buffer.new() | ||||||
| 	self._buffer:wu8(Serdes(Identifier)) | 	self._buffer:wu8(Serdes.increment(Identifier)) | ||||||
| 	self.id = Buffer.convert(self._buffer:build()) | 	self.id = Buffer.convert(self._buffer:build()) | ||||||
| 	self.fn = {} | 	self.fn = {} | ||||||
| 	self._conf = table.freeze(conf or {}) | 	self._conf = table.freeze(conf or {}) | ||||||
|  | @ -30,11 +30,6 @@ function Server.new(Identifier: string, conf: Type.ServerConf?) | ||||||
| 	return self | 	return self | ||||||
| end | end | ||||||
| 
 | 
 | ||||||
| function Server:logs() |  | ||||||
| 	Assert(self._conf.logging, "[Server]: Event is not configured with logging.") |  | ||||||
| 	return ServerProcess.getlogs(self.id) |  | ||||||
| end |  | ||||||
| 
 |  | ||||||
| function Server:Fire(reliable: boolean, player: Player, ...: any) | function Server:Fire(reliable: boolean, player: Player, ...: any) | ||||||
| 	ServerProcess.insertQueue(self.id, reliable, player, ...) | 	ServerProcess.insertQueue(self.id, reliable, player, ...) | ||||||
| end | end | ||||||
|  | @ -75,9 +70,9 @@ function Server:Once(callback: (plyer: Player, args: any) -> ()): string | ||||||
| 	local key = tostring(Key()) | 	local key = tostring(Key()) | ||||||
| 	table.insert(self.fn, key) | 	table.insert(self.fn, key) | ||||||
| 	self.IsConnected = #self.fn > 0 | 	self.IsConnected = #self.fn > 0 | ||||||
| 	ServerProcess.addCallback(self.id, key, function(...) | 	ServerProcess.addCallback(self.id, key, function(player: Player, ...: any?) | ||||||
| 		self:Disconnect(key) | 		self:Disconnect(key) | ||||||
| 		task.spawn(callback, ...) | 		task.spawn(callback, player, ...) | ||||||
| 	end) | 	end) | ||||||
| 	return key | 	return key | ||||||
| end | end | ||||||
|  | @ -106,6 +101,10 @@ end | ||||||
| 
 | 
 | ||||||
| function Server:Destroy() | function Server:Destroy() | ||||||
| 	self:DisconnectAll() | 	self:DisconnectAll() | ||||||
|  | 	self._buffer:remove() | ||||||
|  | 	ServerProcess.remove(self.id) | ||||||
|  | 	Serdes.decrement() | ||||||
|  | 	table.clear(self) | ||||||
| 	setmetatable(self, nil) | 	setmetatable(self, nil) | ||||||
| end | end | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,34 +0,0 @@ | ||||||
| --!strict |  | ||||||
| --!native |  | ||||||
| --!optimize 2 |  | ||||||
| local Logger = {} |  | ||||||
| local Logs: { |  | ||||||
| 	[string]: { |  | ||||||
| 		[string]: string |  | ||||||
| 	} |  | ||||||
| } = {} |  | ||||||
| local logging: { |  | ||||||
| 	[string]: boolean |  | ||||||
| } = {} |  | ||||||
| 
 |  | ||||||
| local now = tick() |  | ||||||
| 
 |  | ||||||
| function Logger.write(Identifier: string, text: string, log: boolean?) |  | ||||||
| 	if not Logs[Identifier] then |  | ||||||
| 		Logs[Identifier] = {} |  | ||||||
| 	end |  | ||||||
| 	if log ~= nil then |  | ||||||
| 		logging[Identifier] = log |  | ||||||
| 	end |  | ||||||
| 	now = tick() |  | ||||||
| 	Logs[Identifier][tostring(now)] = text |  | ||||||
| 	if logging[Identifier] then |  | ||||||
| 		print(`[{now}] ->`, text) |  | ||||||
| 	end |  | ||||||
| end |  | ||||||
| 
 |  | ||||||
| function Logger.read(Identifier: string) |  | ||||||
| 	return Logs[Identifier] |  | ||||||
| end |  | ||||||
| 
 |  | ||||||
| return Logger |  | ||||||
|  | @ -13,24 +13,18 @@ local Spawn = require(Util.Spawn) | ||||||
| local Key = require(Util.Key) | local Key = require(Util.Key) | ||||||
| local RateLimit = require(Util.RateLimit) | local RateLimit = require(Util.RateLimit) | ||||||
| local Buffer = require(Util.Buffer) | local Buffer = require(Util.Buffer) | ||||||
| local Logger = require(script.Logger) |  | ||||||
| 
 | 
 | ||||||
| local serverQueue: Type.QueueMap = {} | local serverQueue: Type.QueueMap = {} | ||||||
| local unreliableServerQueue: Type.QueueMap = {} | local unreliableServerQueue: Type.QueueMap = {} | ||||||
| local serverCallback: Type.CallbackMap = {} | local serverCallback: Type.CallbackMap = {} | ||||||
| local serverRequestQueue: Type.QueueMap = {} | local serverRequestQueue: Type.QueueMap = {} | ||||||
| local registeredIdentifier: { string } = {} | local registeredIdentifier: { [string]: boolean } = {} | ||||||
| 
 | 
 | ||||||
| local queueOut: { | local queueOut: { | ||||||
| 	[Player]: { | 	[Player]: { | ||||||
| 		[string]: {any}, | 		[string]: {any}, | ||||||
| 	} | 	} | ||||||
| } = {} | } = {} | ||||||
| local queueIn: { |  | ||||||
| 	[string]: { |  | ||||||
| 		[Player]: {any}, |  | ||||||
| 	} |  | ||||||
| } = {} |  | ||||||
| local queueInRequest: { | local queueInRequest: { | ||||||
| 	[number]: { | 	[number]: { | ||||||
| 		[string]: { | 		[string]: { | ||||||
|  | @ -45,12 +39,6 @@ local queueOutRequest: { | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| } = {} | } = {} | ||||||
| local logger: { |  | ||||||
| 	[string]: boolean |  | ||||||
| } = {} |  | ||||||
| local players: { |  | ||||||
| 	Player |  | ||||||
| } = {} |  | ||||||
| 
 | 
 | ||||||
| queueInRequest[1] = {} | queueInRequest[1] = {} | ||||||
| queueInRequest[2] = {} | queueInRequest[2] = {} | ||||||
|  | @ -61,22 +49,29 @@ local ReliableEvent = Event.Reliable | ||||||
| local UnreliableEvent = Event.Unreliable | local UnreliableEvent = Event.Unreliable | ||||||
| local RequestEvent = Event.Request | local RequestEvent = Event.Request | ||||||
| 
 | 
 | ||||||
|  | RateLimit.Protect() | ||||||
|  | 
 | ||||||
| local function initializeEachPlayer(player: Player) | local function initializeEachPlayer(player: Player) | ||||||
| 	if not player then return end | 	if not player then return end | ||||||
| 	if not queueOut[player] then | 	if not queueOut[player] then | ||||||
| 		queueOut[player] = {} | 		queueOut[player] = {} | ||||||
| 	end | 	end | ||||||
| 
 | 	for Identifier: string in registeredIdentifier do | ||||||
| 	for _, Identifier: string in registeredIdentifier do |  | ||||||
| 		if not player then break end | 		if not player then break end | ||||||
| 		if not queueOut[player][Identifier] then | 		if not queueOut[player][Identifier] then | ||||||
| 			queueOut[player][Identifier] = {} | 			queueOut[player][Identifier] = {} | ||||||
| 		end | 		end | ||||||
|  | 		if not serverRequestQueue[Identifier] then | ||||||
|  | 			serverRequestQueue[Identifier] = {} | ||||||
|  | 		end | ||||||
| 		if not serverRequestQueue[Identifier][player] then | 		if not serverRequestQueue[Identifier][player] then | ||||||
| 			serverRequestQueue[Identifier][player] = {} | 			serverRequestQueue[Identifier][player] = {} | ||||||
| 		end | 		end | ||||||
| 		if not queueIn[Identifier][player] then | 		if not queueOutRequest[1][Identifier] then | ||||||
| 			queueIn[Identifier][player] = {} | 			queueOutRequest[1][Identifier] = {} | ||||||
|  | 		end | ||||||
|  | 		if not queueOutRequest[2][Identifier] then | ||||||
|  | 			queueOutRequest[2][Identifier] = {} | ||||||
| 		end | 		end | ||||||
| 		if not queueInRequest[1][Identifier][player] then | 		if not queueInRequest[1][Identifier][player] then | ||||||
| 			queueInRequest[1][Identifier][player] = {} | 			queueInRequest[1][Identifier][player] = {} | ||||||
|  | @ -90,15 +85,44 @@ local function initializeEachPlayer(player: Player) | ||||||
| end | end | ||||||
| 
 | 
 | ||||||
| Players.PlayerAdded:Connect(initializeEachPlayer) | Players.PlayerAdded:Connect(initializeEachPlayer) | ||||||
|  | Players.PlayerRemoving:Connect(function(player: Player) | ||||||
|  | 	if not player then return end | ||||||
|  | 	if queueOut[player] then | ||||||
|  | 		queueOut[player] = nil | ||||||
|  | 	end | ||||||
|  | 	for _, map in { serverQueue, unreliableServerQueue, serverRequestQueue } do | ||||||
|  | 		for Identifier: string in map do | ||||||
|  | 			map[Identifier][player] = nil | ||||||
|  | 		end | ||||||
|  | 	end | ||||||
|  | 	for i=1,2 do | ||||||
|  | 		for Identifier: string in queueInRequest[i] do | ||||||
|  | 			if queueInRequest[i][Identifier][player] then | ||||||
|  | 				queueInRequest[i][Identifier][player] = nil | ||||||
|  | 			end | ||||||
|  | 		end | ||||||
|  | 		for Identifier: string in queueOutRequest[i] do | ||||||
|  | 			if queueOutRequest[i][Identifier][player] then | ||||||
|  | 				queueOutRequest[i][Identifier][player] = nil | ||||||
|  | 			end | ||||||
|  | 		end | ||||||
|  | 	end | ||||||
|  | end) | ||||||
| 
 | 
 | ||||||
| function ServerProcess.insertQueue(Identifier: string, reliable: boolean, player: Player, ...: any) | function ServerProcess.insertQueue(Identifier: string, reliable: boolean, player: Player, ...: any) | ||||||
| 	if not reliable then | 	if not reliable then | ||||||
|  | 		if not unreliableServerQueue[Identifier] then | ||||||
|  | 			unreliableServerQueue[Identifier] = {} | ||||||
|  | 		end | ||||||
| 		if not unreliableServerQueue[Identifier][player] then | 		if not unreliableServerQueue[Identifier][player] then | ||||||
| 			unreliableServerQueue[Identifier][player] = {} | 			unreliableServerQueue[Identifier][player] = {} | ||||||
| 		end | 		end | ||||||
| 		table.insert(unreliableServerQueue[Identifier][player], { ... }) | 		table.insert(unreliableServerQueue[Identifier][player], { ... }) | ||||||
| 		return | 		return | ||||||
| 	end | 	end | ||||||
|  | 	if not serverQueue[Identifier] then | ||||||
|  | 		serverQueue[Identifier] = {} | ||||||
|  | 	end | ||||||
| 	if not serverQueue[Identifier][player] then | 	if not serverQueue[Identifier][player] then | ||||||
| 		serverQueue[Identifier][player] = {} | 		serverQueue[Identifier][player] = {} | ||||||
| 	end | 	end | ||||||
|  | @ -106,12 +130,12 @@ function ServerProcess.insertQueue(Identifier: string, reliable: boolean, player | ||||||
| end | end | ||||||
| 
 | 
 | ||||||
| function ServerProcess.insertRequest(Identifier: string, timeout: number, player: Player, ...: any) | function ServerProcess.insertRequest(Identifier: string, timeout: number, player: Player, ...: any) | ||||||
| 	if not serverQueue[Identifier][player] then |  | ||||||
| 		serverQueue[Identifier][player] = {} |  | ||||||
| 	end |  | ||||||
| 	if not serverRequestQueue[Identifier] then | 	if not serverRequestQueue[Identifier] then | ||||||
| 		serverRequestQueue[Identifier] = {} | 		serverRequestQueue[Identifier] = {} | ||||||
| 	end | 	end | ||||||
|  | 	if not serverRequestQueue[Identifier][player] then | ||||||
|  | 		serverRequestQueue[Identifier][player] = {} | ||||||
|  | 	end | ||||||
| 	local yieldThread: thread, start = coroutine.running(), os.clock() | 	local yieldThread: thread, start = coroutine.running(), os.clock() | ||||||
| 	local cancel = task.delay(timeout, function() | 	local cancel = task.delay(timeout, function() | ||||||
| 		task.spawn(yieldThread, nil) | 		task.spawn(yieldThread, nil) | ||||||
|  | @ -125,12 +149,11 @@ function ServerProcess.insertRequest(Identifier: string, timeout: number, player | ||||||
| end | end | ||||||
| 
 | 
 | ||||||
| function ServerProcess.add(Identifier: string, originId: string, conf: Type.ServerConf) | function ServerProcess.add(Identifier: string, originId: string, conf: Type.ServerConf) | ||||||
| 	if not table.find(registeredIdentifier, Identifier) then | 	if not registeredIdentifier[Identifier] then | ||||||
| 		table.insert(registeredIdentifier, Identifier) | 		registeredIdentifier[Identifier] = true | ||||||
|  | 
 | ||||||
| 		RateLimit.create(originId, conf.rateLimit and conf.rateLimit.maxEntrance or 200, conf.rateLimit and conf.rateLimit.interval or 2) | 		RateLimit.create(originId, conf.rateLimit and conf.rateLimit.maxEntrance or 200, conf.rateLimit and conf.rateLimit.interval or 2) | ||||||
| 		if conf.logging then | 
 | ||||||
| 			ServerProcess.logger(Identifier, conf.logging.store, conf.logging.opt) |  | ||||||
| 		end |  | ||||||
| 		if not serverQueue[Identifier] then | 		if not serverQueue[Identifier] then | ||||||
| 			serverQueue[Identifier] = {} | 			serverQueue[Identifier] = {} | ||||||
| 		end | 		end | ||||||
|  | @ -144,9 +167,6 @@ function ServerProcess.add(Identifier: string, originId: string, conf: Type.Serv | ||||||
| 			unreliableServerQueue[Identifier] = {} | 			unreliableServerQueue[Identifier] = {} | ||||||
| 		end | 		end | ||||||
| 
 | 
 | ||||||
| 		if not queueIn[Identifier] then |  | ||||||
| 			queueIn[Identifier] = {} |  | ||||||
| 		end |  | ||||||
| 		if not queueInRequest[1][Identifier] then | 		if not queueInRequest[1][Identifier] then | ||||||
| 			queueInRequest[1][Identifier] = {} | 			queueInRequest[1][Identifier] = {} | ||||||
| 		end | 		end | ||||||
|  | @ -166,124 +186,130 @@ function ServerProcess.add(Identifier: string, originId: string, conf: Type.Serv | ||||||
| 	end | 	end | ||||||
| end | end | ||||||
| 
 | 
 | ||||||
| function ServerProcess.logger(Identifier: string, store: boolean, log: boolean) | function ServerProcess.remove(Identifier: string) | ||||||
| 	logger[Identifier] = store | 	if not registeredIdentifier[Identifier] then return end | ||||||
| 	Logger.write(Identifier, `state: change -> {log == true and "enabled" or "disabled"} logger.`, log) | 	registeredIdentifier[Identifier] = nil | ||||||
| end | 	serverQueue[Identifier] = nil | ||||||
| 
 | 	serverRequestQueue[Identifier] = nil | ||||||
| function ServerProcess.getlogs(Identifier: string) | 	serverCallback[Identifier] = nil | ||||||
| 	return Logger.read(Identifier) | 	unreliableServerQueue[Identifier] = nil | ||||||
|  | 	queueInRequest[1][Identifier] = nil | ||||||
|  | 	queueInRequest[2][Identifier] = nil | ||||||
|  | 	queueOutRequest[1][Identifier] = nil | ||||||
|  | 	queueOutRequest[2][Identifier] = nil | ||||||
| end | end | ||||||
| 
 | 
 | ||||||
| function ServerProcess.addCallback(Identifier: string, key: string, callback) | function ServerProcess.addCallback(Identifier: string, key: string, callback) | ||||||
| 	serverCallback[Identifier][key] = callback | 	serverCallback[Identifier][key] = callback | ||||||
| 	if logger[Identifier] then |  | ||||||
| 		task.defer(Logger.write, Identifier, `state: change -> new callback added.`) |  | ||||||
| 	end |  | ||||||
| end | end | ||||||
| 
 | 
 | ||||||
| function ServerProcess.removeCallback(Identifier: string, key: string) | function ServerProcess.removeCallback(Identifier: string, key: string) | ||||||
| 	serverCallback[Identifier][key] = nil | 	serverCallback[Identifier][key] = nil | ||||||
| 	if logger[Identifier] then |  | ||||||
| 		task.defer(Logger.write, Identifier, `state: change -> removed a callback.`) |  | ||||||
| 	end |  | ||||||
| end | end | ||||||
| 
 | 
 | ||||||
| function ServerProcess.start() | function ServerProcess.start() | ||||||
| 	debug.setmemorycategory("Warp") | 	debug.setmemorycategory("Warp") | ||||||
| 	RunService.PostSimulation:Connect(function() | 	RunService.PostSimulation:Connect(function() | ||||||
|  | 		-- Unreliable | ||||||
| 		for Identifier: string, players in unreliableServerQueue do | 		for Identifier: string, players in unreliableServerQueue do | ||||||
| 			for player: Player, content: any in players do | 			for player: Player, content: any in players do | ||||||
| 				if #content == 0 then continue end | 				if #content == 0 then continue end | ||||||
| 				UnreliableEvent:FireClient(player, Buffer.revert(Identifier), content) | 				for _, unpacked in content do | ||||||
| 				if logger[Identifier] then | 					UnreliableEvent:FireClient(player, Buffer.revert(Identifier), Buffer.write(unpacked)) | ||||||
| 					task.defer(Logger.write, Identifier, `state: out -> unreliable -> {#content} data.`) |  | ||||||
| 				end | 				end | ||||||
| 				unreliableServerQueue[Identifier][player] = nil | 				unreliableServerQueue[Identifier][player] = nil | ||||||
| 			end | 			end | ||||||
|  | 			unreliableServerQueue[Identifier] = nil | ||||||
| 		end | 		end | ||||||
| 
 | 		-- Reliable | ||||||
| 		for Identifier: string, contents: { [Player]: { any } } in serverQueue do | 		for Identifier: string, contents: { [Player]: { any } } in serverQueue do | ||||||
| 			 | 			for player, content: any in contents do | ||||||
| 			for player: Player, requestsData: any in queueOutRequest[1][Identifier] do | 				if #content > 0 and queueOut[player] then | ||||||
|  | 					for _, unpacked in content do | ||||||
|  | 						ReliableEvent:FireClient(player, Buffer.revert(Identifier), Buffer.write(unpacked)) | ||||||
|  | 					end | ||||||
|  | 				end | ||||||
|  | 				serverQueue[Identifier][player] = nil | ||||||
|  | 			end | ||||||
|  | 			serverQueue[Identifier] = nil | ||||||
|  | 		end | ||||||
|  | 		-- Sent new invokes | ||||||
|  | 		for Identifier: string, contents in queueOutRequest[1] do | ||||||
|  | 			for player: Player, requestsData: any in contents do | ||||||
| 				if #requestsData > 0 then | 				if #requestsData > 0 then | ||||||
| 					RequestEvent:FireClient(player, Buffer.revert(Identifier), "\1", requestsData) | 					RequestEvent:FireClient(player, Buffer.revert(Identifier), "\1", requestsData) | ||||||
| 					if logger[Identifier] then |  | ||||||
| 						task.defer(Logger.write, Identifier, `state: out -> request -> {#requestsData} data.`) |  | ||||||
| 					end |  | ||||||
| 				end | 				end | ||||||
| 				queueOutRequest[1][Identifier][player] = nil | 				queueOutRequest[1][Identifier][player] = nil | ||||||
| 			end | 			end | ||||||
| 			 | 			queueOutRequest[1][Identifier] = nil | ||||||
| 			for player: Player, toReturnDatas: any in queueOutRequest[2][Identifier] do | 		end | ||||||
|  | 		-- Sent returning invokes | ||||||
|  | 		for Identifier: string, contents in queueOutRequest[2] do | ||||||
|  | 			for player: Player, toReturnDatas: any in contents do | ||||||
| 				if #toReturnDatas > 0 then | 				if #toReturnDatas > 0 then | ||||||
| 					RequestEvent:FireClient(player, Buffer.revert(Identifier), "\0", toReturnDatas) | 					RequestEvent:FireClient(player, Buffer.revert(Identifier), "\0", toReturnDatas) | ||||||
| 					if logger[Identifier] then |  | ||||||
| 						task.defer(Logger.write, Identifier, `state: out -> return request -> {#toReturnDatas} data.`) |  | ||||||
| 					end |  | ||||||
| 				end | 				end | ||||||
| 				queueOutRequest[2][Identifier][player] = nil | 				queueOutRequest[2][Identifier][player] = nil | ||||||
| 			end | 			end | ||||||
| 			 | 			queueOutRequest[2][Identifier] = nil | ||||||
| 			local callback = serverCallback[Identifier] or nil |  | ||||||
| 			for player, content: any in contents do |  | ||||||
| 				if #content > 0 and queueOut[player] then |  | ||||||
| 					ReliableEvent:FireClient(player, Buffer.revert(Identifier), content) |  | ||||||
| 		end | 		end | ||||||
| 				serverQueue[Identifier][player] = nil |  | ||||||
| 
 | 
 | ||||||
| 				if serverRequestQueue[Identifier][player] then | 		for Identifier: string in registeredIdentifier do | ||||||
| 					for _, requestData in serverRequestQueue[Identifier][player] do | 			if serverRequestQueue[Identifier] then | ||||||
|  | 				for player, content in serverRequestQueue[Identifier] do | ||||||
|  | 					if #content == 0 then serverRequestQueue[Identifier][player] = nil continue end | ||||||
|  | 					for _, requestData in content do | ||||||
| 						if not requestData[3] then continue end | 						if not requestData[3] then continue end | ||||||
|  | 						if not queueOutRequest[1][Identifier] then | ||||||
|  | 							queueOutRequest[1][Identifier] = {} | ||||||
|  | 						end | ||||||
| 						if not queueOutRequest[1][Identifier][player] then | 						if not queueOutRequest[1][Identifier][player] then | ||||||
| 							queueOutRequest[1][Identifier][player] = {} | 							queueOutRequest[1][Identifier][player] = {} | ||||||
| 						end | 						end | ||||||
| 						table.insert(queueOutRequest[1][Identifier][player], { requestData[1], requestData[3] }) | 						table.insert(queueOutRequest[1][Identifier][player], { requestData[1], requestData[3] }) | ||||||
|  | 						requestData[3] = nil | ||||||
|  | 					end | ||||||
| 				end | 				end | ||||||
| 					serverRequestQueue[Identifier][player] = nil |  | ||||||
| 			end | 			end | ||||||
| 
 | 
 | ||||||
| 				if callback then | 			local callback = serverCallback[Identifier] or nil | ||||||
| 					local requestIn1: any = queueInRequest[1][Identifier][player] | 			if not callback then continue end | ||||||
| 					local requestIn2: any = queueInRequest[2][Identifier][player] |  | ||||||
| 					local incoming: any = queueIn[Identifier][player] |  | ||||||
| 
 | 
 | ||||||
| 					if incoming then | 			-- Return Invoke | ||||||
| 						for _, packedDatas: any in incoming do | 			for player, content in queueInRequest[1][Identifier] do | ||||||
| 							if #packedDatas == 0 then continue end | 				if not callback then break end | ||||||
| 							for _, fn: any in callback do | 				for _, packetDatas in content do | ||||||
| 								for i=1,#packedDatas do | 					if not callback then break end | ||||||
| 									Spawn(fn, player, table.unpack(packedDatas[i] or {})) |  | ||||||
| 								end |  | ||||||
| 							end |  | ||||||
| 						end |  | ||||||
| 						incoming = nil |  | ||||||
| 						queueIn[Identifier][player] = nil |  | ||||||
| 					end |  | ||||||
| 					if requestIn1 then |  | ||||||
| 						for _, packetDatas: any in requestIn1 do |  | ||||||
| 					if #packetDatas == 0 then continue end | 					if #packetDatas == 0 then continue end | ||||||
| 					for _, fn: any in callback do | 					for _, fn: any in callback do | ||||||
| 						for i=1,#packetDatas do | 						for i=1,#packetDatas do | ||||||
| 									local packetData = packetDatas[i] | 							if not packetDatas[i] then continue end | ||||||
| 									if not packetData then continue end | 							local packetData1 = packetDatas[i][1] | ||||||
|  | 							local packetData2 = packetDatas[i][2] | ||||||
| 							Spawn(function() | 							Spawn(function() | ||||||
| 										local requestReturn = { fn(player, table.unpack(packetData[2])) } | 								local requestReturn = { fn(player, table.unpack(packetData2)) } | ||||||
| 										local state = queueOutRequest[2][Identifier][player] | 								if not queueOutRequest[2][Identifier] then | ||||||
|  | 									queueOutRequest[2][Identifier] = {} | ||||||
|  | 								end | ||||||
| 								if not queueOutRequest[2][Identifier][player] then | 								if not queueOutRequest[2][Identifier][player] then | ||||||
| 									queueOutRequest[2][Identifier][player] = {} | 									queueOutRequest[2][Identifier][player] = {} | ||||||
| 								end | 								end | ||||||
| 										table.insert(queueOutRequest[2][Identifier][player], { packetData[1], requestReturn }) | 								table.insert(queueOutRequest[2][Identifier][player], { packetData1, requestReturn }) | ||||||
|  | 								packetData1 = nil | ||||||
|  | 								packetData2 = nil | ||||||
| 							end) | 							end) | ||||||
| 						end | 						end | ||||||
| 					end | 					end | ||||||
| 				end | 				end | ||||||
| 						requestIn1 = nil |  | ||||||
| 				queueInRequest[1][Identifier][player] = nil | 				queueInRequest[1][Identifier][player] = nil | ||||||
| 			end | 			end | ||||||
| 					if requestIn2 then | 
 | ||||||
| 						for _, packetDatas: any in requestIn2 do | 			-- Call to Invoke | ||||||
|  | 			for player, content in queueInRequest[2][Identifier] do | ||||||
|  | 				if not callback then break end | ||||||
|  | 				for _, packetDatas in content do | ||||||
| 					for _, packetData in packetDatas do | 					for _, packetData in packetDatas do | ||||||
|  | 						if not callback then break end | ||||||
| 						if #packetData == 1 then continue end | 						if #packetData == 1 then continue end | ||||||
| 						local data = serverRequestQueue[Identifier][player] | 						local data = serverRequestQueue[Identifier][player] | ||||||
| 						for i=1,#data do | 						for i=1,#data do | ||||||
|  | @ -297,29 +323,21 @@ function ServerProcess.start() | ||||||
| 						end | 						end | ||||||
| 					end | 					end | ||||||
| 				end | 				end | ||||||
| 						requestIn2 = nil |  | ||||||
| 				queueInRequest[2][Identifier][player] = nil | 				queueInRequest[2][Identifier][player] = nil | ||||||
| 			end | 			end | ||||||
| 		end | 		end | ||||||
| 			end |  | ||||||
| 		end |  | ||||||
| 	end) | 	end) | ||||||
| 	local function onServerNetworkReceive(player: Player, Identifier: any, data: any) | 	local function onServerNetworkReceive(player: Player, Identifier: buffer | string, data: buffer, ref: { any }?) | ||||||
| 		if not Identifier or not data then return end | 		if not Identifier or typeof(Identifier) ~= "buffer" or not data or typeof(data) ~= "buffer" then return end | ||||||
| 		Identifier = Buffer.convert(Identifier) | 		Identifier = Buffer.convert(Identifier :: buffer) | ||||||
| 		if not serverQueue[Identifier] then | 		if not registeredIdentifier[Identifier :: string] then return end | ||||||
| 			serverQueue[Identifier] = {} | 		local read = Buffer.read(data, ref) | ||||||
|  | 		if not read then return end | ||||||
|  | 		local callback = serverCallback[Identifier :: string] | ||||||
|  | 		if not callback then return end | ||||||
|  | 		for _, fn: any in callback do | ||||||
|  | 			Spawn(fn, player, table.unpack(read)) | ||||||
| 		end | 		end | ||||||
| 		if not serverQueue[Identifier][player] then |  | ||||||
| 			serverQueue[Identifier][player] = {} |  | ||||||
| 		end |  | ||||||
| 		if not queueIn[Identifier][player] then |  | ||||||
| 			queueIn[Identifier][player] = {} |  | ||||||
| 		end |  | ||||||
| 		if logger[Identifier] then |  | ||||||
| 			task.defer(Logger.write, Identifier, `state: in -> net -> {#data} data.`) |  | ||||||
| 		end |  | ||||||
| 		table.insert(queueIn[Identifier][player], data) |  | ||||||
| 	end | 	end | ||||||
| 	ReliableEvent.OnServerEvent:Connect(onServerNetworkReceive) | 	ReliableEvent.OnServerEvent:Connect(onServerNetworkReceive) | ||||||
| 	UnreliableEvent.OnServerEvent:Connect(onServerNetworkReceive) | 	UnreliableEvent.OnServerEvent:Connect(onServerNetworkReceive) | ||||||
|  | @ -328,8 +346,13 @@ function ServerProcess.start() | ||||||
| 		Identifier = Buffer.convert(Identifier) | 		Identifier = Buffer.convert(Identifier) | ||||||
| 		if not queueInRequest[1][Identifier][player] then | 		if not queueInRequest[1][Identifier][player] then | ||||||
| 			queueInRequest[1][Identifier][player] = {} | 			queueInRequest[1][Identifier][player] = {} | ||||||
|  | 		end | ||||||
|  | 		if not queueInRequest[2][Identifier][player] then | ||||||
| 			queueInRequest[2][Identifier][player] = {} | 			queueInRequest[2][Identifier][player] = {} | ||||||
| 		end | 		end | ||||||
|  | 		if not serverQueue[Identifier] then | ||||||
|  | 			serverQueue[Identifier] = {} | ||||||
|  | 		end | ||||||
| 		if not serverQueue[Identifier][player] then | 		if not serverQueue[Identifier][player] then | ||||||
| 			serverQueue[Identifier][player] = {} | 			serverQueue[Identifier][player] = {} | ||||||
| 		end | 		end | ||||||
|  | @ -338,9 +361,6 @@ function ServerProcess.start() | ||||||
| 		else | 		else | ||||||
| 			table.insert(queueInRequest[2][Identifier][player], data) | 			table.insert(queueInRequest[2][Identifier][player], data) | ||||||
| 		end | 		end | ||||||
| 		if logger[Identifier] then |  | ||||||
| 			task.defer(Logger.write, Identifier, `state: in -> request -> {#data} data.`) |  | ||||||
| 		end |  | ||||||
| 	end) | 	end) | ||||||
| end | end | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -12,6 +12,7 @@ function Dedicated.new(signal: any, handler: (...any) -> ()) | ||||||
| end | end | ||||||
| 
 | 
 | ||||||
| function Dedicated:Disconnect() | function Dedicated:Disconnect() | ||||||
|  | 	table.clear(self) | ||||||
| 	setmetatable(self, nil) | 	setmetatable(self, nil) | ||||||
| end | end | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -22,15 +22,15 @@ function Signal.new(Identifier: string) | ||||||
| 	return Signals[Identifier] | 	return Signals[Identifier] | ||||||
| end | end | ||||||
| 
 | 
 | ||||||
| function Signal:Connect(fn: (...any) -> ()): string | function Signal:Connect(fn: (...any) -> (), optKey: string?): string | ||||||
| 	local key = tostring(Key()) | 	local key: typeof(Signal) = optKey or tostring(Key()) :: any | ||||||
| 	self[key] = DedicatedSignal(self, fn) | 	self[key] = DedicatedSignal(self, fn) | ||||||
| 	return key | 	return key :: any | ||||||
| end | end | ||||||
| 
 | 
 | ||||||
| function Signal:Once(fn: (...any) -> ()): string | function Signal:Once(fn: (...any) -> ()): string | ||||||
| 	local key: string | 	local key: string | ||||||
| 	key = self:Connect(function(...) | 	key = self:Connect(function(...: any) | ||||||
| 		self:Disconnect(key) | 		self:Disconnect(key) | ||||||
| 		task.spawn(fn, ...) | 		task.spawn(fn, ...) | ||||||
| 	end) | 	end) | ||||||
|  | @ -38,7 +38,9 @@ function Signal:Once(fn: (...any) -> ()): string | ||||||
| end | end | ||||||
| 
 | 
 | ||||||
| function Signal:Disconnect(key: string) | function Signal:Disconnect(key: string) | ||||||
|  | 	if not self[key] then return end | ||||||
| 	self[key]:Disconnect() | 	self[key]:Disconnect() | ||||||
|  | 	self[key] = nil | ||||||
| end | end | ||||||
| 
 | 
 | ||||||
| function Signal:DisconnectAll(): () | function Signal:DisconnectAll(): () | ||||||
|  | @ -46,13 +48,19 @@ function Signal:DisconnectAll(): () | ||||||
| end | end | ||||||
| 
 | 
 | ||||||
| function Signal:Wait(): number | function Signal:Wait(): number | ||||||
| 	local thread, t = coroutine.running(), os.clock() | 	local t, thread = os.clock(), coroutine.running() | ||||||
| 	self:Once(function() | 	self:Once(function() | ||||||
| 		task.spawn(thread, os.clock()-t) | 		task.spawn(thread, os.clock()-t) | ||||||
| 	end) | 	end) | ||||||
| 	return coroutine.yield() | 	return coroutine.yield() | ||||||
| end | end | ||||||
| 
 | 
 | ||||||
|  | function Signal:DeferFire(...: any): () | ||||||
|  | 	for _, handle in self do | ||||||
|  | 		task.defer(handle.fn, ...) | ||||||
|  | 	end | ||||||
|  | end | ||||||
|  | 
 | ||||||
| function Signal:Fire(...: any): () | function Signal:Fire(...: any): () | ||||||
| 	for _, handle in self do | 	for _, handle in self do | ||||||
| 		task.spawn(handle.fn, ...) | 		task.spawn(handle.fn, ...) | ||||||
|  |  | ||||||
|  | @ -4,44 +4,41 @@ type rateLimitArg = { | ||||||
| 	interval: number?, | 	interval: number?, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type logging = { |  | ||||||
| 	store: boolean, |  | ||||||
| 	opt: boolean, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| export type ServerConf = { | export type ServerConf = { | ||||||
| 	rateLimit: rateLimitArg?, | 	rateLimit: rateLimitArg?, | ||||||
| 	logging: logging?, |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export type ClientConf = { | export type ClientConf = { | ||||||
| 	yieldWait: number?, | 	yieldWait: number?, | ||||||
| 	logging: logging?, | } | ||||||
|  | 
 | ||||||
|  | export type Middleware = { | ||||||
|  | 	middleware: (self: Middleware, middleware: (...any) -> (...any)) -> (), | ||||||
|  | 	key: (self: Middleware) -> string, | ||||||
|  | 	destroy: (self: Middleware) -> (), | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export type Client = { | export type Client = { | ||||||
| 	Fire: (self: Client, reliable: boolean, ...any) -> (), | 	Fire: (self: Client, reliable: boolean, ...any) -> (), | ||||||
| 	Invoke: (self: Client, timeout: number, ...any) -> any, | 	Invoke: (self: Client, timeout: number, ...any) -> any, | ||||||
| 	Connect: (self: Client, callback: (...any) -> ()) -> string, | 	Connect: (self: Client, callback: (...any) -> ()) -> Middleware, | ||||||
| 	Once: (self: Client, callback: (player: Player, ...any) -> ()) -> string, | 	Once: (self: Client, callback: (player: Player, ...any) -> ()) -> Middleware, | ||||||
| 	Disconnect: (self: Client, key: string) -> (), | 	Disconnect: (self: Client, key: string) -> (), | ||||||
| 	DisconnectAll: (self: Client) -> (), | 	DisconnectAll: (self: Client) -> (), | ||||||
| 	Wait: (self: Client) -> number, | 	Wait: (self: Client) -> number, | ||||||
| 	Destroy: (self: Client) -> (), | 	Destroy: (self: Client) -> (), | ||||||
| 	logs: (self: Client) -> any, |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export type Server = { | export type Server = { | ||||||
| 	Fire: (self: Server, reliable: boolean, player: Player, ...any) -> (), | 	Fire: (self: Server, reliable: boolean, player: Player, ...any) -> (), | ||||||
| 	Fires: (self: Server, reliable: boolean, ...any) -> (), | 	Fires: (self: Server, reliable: boolean, ...any) -> (), | ||||||
| 	Invoke: (self: Server, timeout: number, player: Player, ...any) -> any, | 	Invoke: (self: Server, timeout: number, player: Player, ...any) -> any, | ||||||
| 	Connect: (self: Server, callback: (player: Player, ...any) -> ()) -> string, | 	Connect: (self: Server, callback: (player: Player, ...any) -> ()) -> Middleware, | ||||||
| 	Once: (self: Server, callback: (player: Player, ...any) -> ()) -> string, | 	Once: (self: Server, callback: (player: Player, ...any) -> ()) -> Middleware, | ||||||
| 	Disconnect: (self: Server, key: string) -> (), | 	Disconnect: (self: Server, key: string) -> (), | ||||||
| 	DisconnectAll: (self: Server) -> (), | 	DisconnectAll: (self: Server) -> (), | ||||||
| 	Wait: (self: Server) -> number, | 	Wait: (self: Server) -> number, | ||||||
| 	Destroy: (self: Server) -> (), | 	Destroy: (self: Server) -> (), | ||||||
| 	logs: (self: Server) -> any, |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export type Signal = { | export type Signal = { | ||||||
|  |  | ||||||
|  | @ -1,6 +1,6 @@ | ||||||
| --!strict | --!strict | ||||||
| --!native | --!native | ||||||
| --!optimize 2 | --!optimize 2 | ||||||
| return function(condition: (any), errorMessage: string?): () | return function(condition: (any), errorMessage: string, level: number?): () | ||||||
| 	if not (condition) then error(`Warp: {errorMessage}`, 2) end | 	if not (condition) then error(`Warp: {errorMessage}`, level or 2) end | ||||||
| end | end | ||||||
|  | @ -16,7 +16,7 @@ local writef32 = buffer.writef32 | ||||||
| local writef64 = buffer.writef64 | local writef64 = buffer.writef64 | ||||||
| local writestring = buffer.writestring | local writestring = buffer.writestring | ||||||
| 
 | 
 | ||||||
| local default = { | local default: { [string]: number } = { | ||||||
| 	point = 0, | 	point = 0, | ||||||
| 	next = 0, | 	next = 0, | ||||||
| 	size = 128, | 	size = 128, | ||||||
|  | @ -35,17 +35,16 @@ function DedicatedBuffer.alloc(self: any, byte: number) | ||||||
| 	local size: number = self.size | 	local size: number = self.size | ||||||
| 	local b: buffer = self.buffer | 	local b: buffer = self.buffer | ||||||
| 
 | 
 | ||||||
| 
 | 	while self.next + byte >= size do | ||||||
| 	while self.point + byte >= size do | 		size = math.floor(size * 1.25) -- +25% increase | ||||||
| 		size = math.floor(size * 1.5) |  | ||||||
| 	end | 	end | ||||||
| 
 |  | ||||||
| 	local newBuffer: buffer = create(size) | 	local newBuffer: buffer = create(size) | ||||||
| 	copy(newBuffer, 0, b) | 	copy(newBuffer, 0, b) | ||||||
| 
 | 
 | ||||||
| 	b = newBuffer | 	b = newBuffer | ||||||
| 
 | 
 | ||||||
| 	self.point  = self.next | 	self.point  = self.next | ||||||
|  | 	self.buffer = b | ||||||
| 	self.next += byte | 	self.next += byte | ||||||
| end | end | ||||||
| 
 | 
 | ||||||
|  | @ -57,51 +56,62 @@ function DedicatedBuffer.build(self: any): buffer | ||||||
| 	return build | 	return build | ||||||
| end | end | ||||||
| 
 | 
 | ||||||
| function DedicatedBuffer.wi8(self: any, val: number) | function DedicatedBuffer.buildAndRemove(self: any): (buffer, (any)?) | ||||||
|  | 	local p: number = self.next > self.point and self.next or self.point | ||||||
|  | 	local build: buffer = create(p) | ||||||
|  | 	local ref = #self.ref > 0 and table.clone(self.ref) or nil | ||||||
|  | 
 | ||||||
|  | 	copy(build, 0, self.buffer, 0, p) | ||||||
|  | 
 | ||||||
|  | 	self:remove() | ||||||
|  | 	return build, ref | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | function DedicatedBuffer.wi8(self: any, val: number, alloc: number?) | ||||||
| 	if not val then return end | 	if not val then return end | ||||||
| 	self:alloc(1) | 	self:alloc(alloc or 1) | ||||||
| 	writei8(self.buffer, self.point, val) | 	writei8(self.buffer, self.point, val) | ||||||
| end | end | ||||||
| 
 | 
 | ||||||
| function DedicatedBuffer.wi16(self: any, val: number) | function DedicatedBuffer.wi16(self: any, val: number, alloc: number?) | ||||||
| 	if not val then return end | 	if not val then return end | ||||||
| 	self:alloc(2) | 	self:alloc(alloc or 2) | ||||||
| 	writei16(self.buffer, self.point, val) | 	writei16(self.buffer, self.point, val) | ||||||
| end | end | ||||||
| 
 | 
 | ||||||
| function DedicatedBuffer.wi32(self: any, val: number) | function DedicatedBuffer.wi32(self: any, val: number, alloc: number?) | ||||||
| 	if not val then return end | 	if not val then return end | ||||||
| 	self:alloc(4) | 	self:alloc(alloc or 4) | ||||||
| 	writei32(self.buffer, self.point, val) | 	writei32(self.buffer, self.point, val) | ||||||
| end | end | ||||||
| 
 | 
 | ||||||
| function DedicatedBuffer.wu8(self: any, val: number) | function DedicatedBuffer.wu8(self: any, val: number, alloc: number?) | ||||||
| 	if not val then return end | 	if not val then return end | ||||||
| 	self:alloc(1) | 	self:alloc(alloc or 1) | ||||||
| 	writeu8(self.buffer, self.point, val) | 	writeu8(self.buffer, self.point, val) | ||||||
| end | end | ||||||
| 
 | 
 | ||||||
| function  DedicatedBuffer.wu16(self: any, val: number) | function  DedicatedBuffer.wu16(self: any, val: number, alloc: number?) | ||||||
| 	if not val then return end | 	if not val then return end | ||||||
| 	self:alloc(2) | 	self:alloc(alloc or 2) | ||||||
| 	writeu16(self.buffer, self.point, val) | 	writeu16(self.buffer, self.point, val) | ||||||
| end | end | ||||||
| 
 | 
 | ||||||
| function DedicatedBuffer.wu32(self: any, val: number) | function DedicatedBuffer.wu32(self: any, val: number, alloc: number?) | ||||||
| 	if not val then return end | 	if not val then return end | ||||||
| 	self:alloc(4) | 	self:alloc(alloc or 4) | ||||||
| 	writeu32(self.buffer, self.point, val) | 	writeu32(self.buffer, self.point, val) | ||||||
| end | end | ||||||
| 
 | 
 | ||||||
| function DedicatedBuffer.wf32(self: any, val: number) | function DedicatedBuffer.wf32(self: any, val: number, alloc: number?) | ||||||
| 	if not val then return end | 	if not val then return end | ||||||
| 	self:alloc(4) | 	self:alloc(alloc or 4) | ||||||
| 	writef32(self.buffer, self.point, val) | 	writef32(self.buffer, self.point, val) | ||||||
| end | end | ||||||
| 
 | 
 | ||||||
| function DedicatedBuffer.wf64(self: any, val: number) | function DedicatedBuffer.wf64(self: any, val: number, alloc: number?) | ||||||
| 	if not val then return end | 	if not val then return end | ||||||
| 	self:alloc(8) | 	self:alloc(alloc or 8) | ||||||
| 	writef64(self.buffer, self.point, val) | 	writef64(self.buffer, self.point, val) | ||||||
| end | end | ||||||
| 
 | 
 | ||||||
|  | @ -111,11 +121,127 @@ function DedicatedBuffer.wstring(self: any, val: string) | ||||||
| 	writestring(self.buffer, self.point, val) | 	writestring(self.buffer, self.point, val) | ||||||
| end | end | ||||||
| 
 | 
 | ||||||
|  | function DedicatedBuffer.wType(self: any, ref: number) | ||||||
|  | 	writeu8(self.buffer, self.point, ref) | ||||||
|  | 	self.point += 1 | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | function DedicatedBuffer.wRef(self: any, value: any, alloc: number?) | ||||||
|  | 	if not value then return end | ||||||
|  | 	self:alloc(alloc or 1) | ||||||
|  | 	table.insert(self.ref, value) | ||||||
|  | 	local index = #self.ref | ||||||
|  | 	writeu8(self.buffer, self.point, index) | ||||||
|  | 	self.point += 1 | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | function DedicatedBuffer.pack(self: any, data: {any}) | ||||||
|  | 	if typeof(data) == "nil" then | ||||||
|  | 		self:wi8(0) | ||||||
|  | 	elseif typeof(data) == "Instance" then | ||||||
|  | 		self:wi8(-1) -- Instance marker | ||||||
|  | 		self:wRef(data) | ||||||
|  | 	elseif typeof(data) == "table" then | ||||||
|  | 		--local isArray = (next(data) ~= nil and #data > 0) and true or false | ||||||
|  | 		local isArray = true | ||||||
|  | 		local count = 0 | ||||||
|  | 		for k in data do | ||||||
|  | 			count += 1 | ||||||
|  | 			if typeof(k) ~= "number" or math.floor(k) ~= k then | ||||||
|  | 				isArray = false | ||||||
|  | 			end | ||||||
|  | 		end | ||||||
|  | 		if isArray then | ||||||
|  | 			self:wi8(-2) -- array marker | ||||||
|  | 			self:wu16(count) -- use 32-bit length | ||||||
|  | 			for _, v in data do | ||||||
|  | 				self:pack(v) | ||||||
|  | 			end | ||||||
|  | 		else | ||||||
|  | 			self:wi8(-3) -- dictionary marker | ||||||
|  | 			self:wu16(count) -- number of key-value pairs | ||||||
|  | 			for k, v in data do | ||||||
|  | 				self:pack(k) -- pack the key | ||||||
|  | 				self:pack(v) -- pack the value | ||||||
|  | 			end | ||||||
|  | 		end | ||||||
|  | 	elseif typeof(data) == "EnumItem" then | ||||||
|  | 		self:wi8(-4) | ||||||
|  | 		self:wi8(#`{data.EnumType}`) | ||||||
|  | 		self:wstring(`{data.EnumType}`) | ||||||
|  | 		self:wu8(data.Value) | ||||||
|  | 	elseif typeof(data) == "BrickColor" then | ||||||
|  | 		self:wi8(-5) | ||||||
|  | 		self:wi16(data.Number) | ||||||
|  | 	elseif typeof(data) == "Enum" then | ||||||
|  | 		self:wi8(-6) | ||||||
|  | 		self:wi8(#`{data}`) | ||||||
|  | 		self:wstring(`{data}`) | ||||||
|  | 	elseif typeof(data) == "number" then | ||||||
|  | 		if math.floor(data) == data then -- Integer | ||||||
|  | 			if data >= 0 and data <= 255 then | ||||||
|  | 				self:wi8(1) -- u8 marker | ||||||
|  | 				self:wu8(data) | ||||||
|  | 			elseif data >= -32768 and data <= 32767 then | ||||||
|  | 				self:wi8(2) -- i16 marker | ||||||
|  | 				self:wi16(data) | ||||||
|  | 			elseif data >= -2147483647 and data <= 2147483647 then | ||||||
|  | 				self:wi8(3) -- i32 marker | ||||||
|  | 				self:wi32(data) | ||||||
|  | 			else | ||||||
|  | 				self:wi8(4) -- f64 marker | ||||||
|  | 				self:wf64(data) | ||||||
|  | 			end | ||||||
|  | 		else | ||||||
|  | 			self:wi8(4) -- f64 marker | ||||||
|  | 			self:wf64(data) | ||||||
|  | 		end | ||||||
|  | 	elseif typeof(data) == "boolean" then | ||||||
|  | 		self:wi8(5) -- boolean marker | ||||||
|  | 		self:wu8(data and 1 or 0) | ||||||
|  | 	elseif typeof(data) == "string" then | ||||||
|  | 		local length = #data | ||||||
|  | 		if length <= 255 then | ||||||
|  | 			self:wi8(6) | ||||||
|  | 			self:wu8(length) | ||||||
|  | 		elseif length <= 65535 then | ||||||
|  | 			self:wi8(7) | ||||||
|  | 			self:wu16(length) | ||||||
|  | 		else | ||||||
|  | 			self:wi8(8) | ||||||
|  | 			self:wi32(length) | ||||||
|  | 		end | ||||||
|  | 		self:wstring(data) | ||||||
|  | 	elseif typeof(data) == "Vector3" then | ||||||
|  | 		self:wi8(9) -- Vector3 marker | ||||||
|  | 		self:wf32(data.X) | ||||||
|  | 		self:wf32(data.Y) | ||||||
|  | 		self:wf32(data.Z) | ||||||
|  | 	elseif typeof(data) == "Vector2" then | ||||||
|  | 		self:wi8(10) -- Vector2 marker | ||||||
|  | 		self:wf32(data.X) | ||||||
|  | 		self:wf32(data.Y) | ||||||
|  | 	elseif typeof(data) == "CFrame" then | ||||||
|  | 		self:wi8(11) -- CFrame marker | ||||||
|  | 		for _, v in {data:GetComponents()} do | ||||||
|  | 			self:wf32(v) | ||||||
|  | 		end | ||||||
|  | 	elseif typeof(data) == "Color3" then | ||||||
|  | 		self:wi8(12) -- Color3 marker | ||||||
|  | 		self:wu8(data.R * 255) | ||||||
|  | 		self:wu8(data.G * 255) | ||||||
|  | 		self:wu8(data.B * 255) | ||||||
|  | 	else | ||||||
|  | 		warn(`Unsupported data type: {typeof(data)} value: {data}`) | ||||||
|  | 	end | ||||||
|  | end | ||||||
|  | 
 | ||||||
| function DedicatedBuffer.flush(self: any) | function DedicatedBuffer.flush(self: any) | ||||||
| 	self.point = default.point | 	self.point = default.point | ||||||
| 	self.next = default.next | 	self.next = default.next | ||||||
| 	self.size = default.size | 	self.size = default.size | ||||||
| 	self.buffer = create(default.bufferSize) | 	self.buffer = create(default.bufferSize) | ||||||
|  | 	table.clear(self.ref) | ||||||
| end | end | ||||||
| 
 | 
 | ||||||
| function DedicatedBuffer.new() | function DedicatedBuffer.new() | ||||||
|  | @ -123,13 +249,17 @@ function DedicatedBuffer.new() | ||||||
| 		point = default.point, | 		point = default.point, | ||||||
| 		next = default.next, | 		next = default.next, | ||||||
| 		size = default.size, | 		size = default.size, | ||||||
| 		buffer = create(default.bufferSize) | 		buffer = create(default.bufferSize), | ||||||
|  | 		ref = {}, | ||||||
| 	}, DedicatedBuffer) | 	}, DedicatedBuffer) | ||||||
| end | end | ||||||
| 
 | 
 | ||||||
| function DedicatedBuffer.remove(self: any) | function DedicatedBuffer.remove(self: any) | ||||||
| 	self:flush() | 	self:flush() | ||||||
|  | 	table.clear(self) | ||||||
| 	setmetatable(self, nil) | 	setmetatable(self, nil) | ||||||
| end | end | ||||||
| 
 | 
 | ||||||
|  | export type DedicatedType = typeof(DedicatedBuffer.new()) | ||||||
|  | 
 | ||||||
| return DedicatedBuffer.new :: typeof(DedicatedBuffer.new) | return DedicatedBuffer.new :: typeof(DedicatedBuffer.new) | ||||||
|  | @ -8,8 +8,115 @@ local Dedicated = require(script.Dedicated) | ||||||
| 
 | 
 | ||||||
| local tostring = buffer.tostring | local tostring = buffer.tostring | ||||||
| local fromstring = buffer.fromstring | local fromstring = buffer.fromstring | ||||||
|  | local readu8 = buffer.readu8 | ||||||
|  | local readi8 = buffer.readi8 | ||||||
|  | local readu16 = buffer.readu16 | ||||||
|  | local readi16 = buffer.readi16 | ||||||
|  | local readi32 = buffer.readi32 | ||||||
|  | local readf32 = buffer.readf32 | ||||||
|  | local readf64 = buffer.readf64 | ||||||
|  | local readstring = buffer.readstring | ||||||
|  | local len = buffer.len | ||||||
| 
 | 
 | ||||||
| function Buffer.new() | local function readValue(b: buffer, position: number, ref: { any }?): (any, number) | ||||||
|  | 	local typeByte = readi8(b, position) | ||||||
|  | 	position += 1 | ||||||
|  | 	if typeByte == 0 then -- nil | ||||||
|  | 		return nil, position | ||||||
|  | 	elseif typeByte == -1 then -- Instance | ||||||
|  | 		if not ref or #ref == 0 then | ||||||
|  | 			return nil, position + 1 | ||||||
|  | 		end | ||||||
|  | 		local value = ref[readu8(b, position)] | ||||||
|  | 		if typeof(value) == "Instance" then | ||||||
|  | 			return value, position + 1 | ||||||
|  | 		end | ||||||
|  | 		return nil, position + 1 | ||||||
|  | 	elseif typeByte == -2 then -- array | ||||||
|  | 		local length = readu16(b, position) | ||||||
|  | 		position += 2 | ||||||
|  | 		local array = {} | ||||||
|  | 		for _ = 1, length do | ||||||
|  | 			local value | ||||||
|  | 			value, position = readValue(b, position, ref) | ||||||
|  | 			table.insert(array, value) | ||||||
|  | 		end | ||||||
|  | 		return array, position | ||||||
|  | 	elseif typeByte == -3 then -- dictionary | ||||||
|  | 		local length = readu16(b, position) | ||||||
|  | 		position += 2 | ||||||
|  | 		local dict = {} | ||||||
|  | 		for _ = 1, length do | ||||||
|  | 			local key, value | ||||||
|  | 			key, position = readValue(b, position, ref) | ||||||
|  | 			value, position = readValue(b, position, ref) | ||||||
|  | 			dict[key] = value | ||||||
|  | 		end | ||||||
|  | 		return dict, position | ||||||
|  | 	elseif typeByte == -4 then -- EnumItem | ||||||
|  | 		local length = readi8(b, position) | ||||||
|  | 		local value = readstring(b, position + 1, length) | ||||||
|  | 		local value2 = readu8(b, position + 1 + length) | ||||||
|  | 		return Enum[value]:FromValue(value2), position + 2 + length | ||||||
|  | 	elseif typeByte == -5 then -- BrickColor | ||||||
|  | 		local value = readi16(b, position) | ||||||
|  | 		return BrickColor.new(value), position + 2 | ||||||
|  | 	elseif typeByte == -6 then -- Enum | ||||||
|  | 		local length = readi8(b, position) | ||||||
|  | 		local value = readstring(b, position + 1, length) | ||||||
|  | 		return Enum[value], position + 1 + length | ||||||
|  | 	elseif typeByte == 1 then -- int u8 | ||||||
|  | 		local value = readu8(b, position) | ||||||
|  | 		return value, position + 1 | ||||||
|  | 	elseif typeByte == 2 then -- int i16 | ||||||
|  | 		local value = readi16(b, position) | ||||||
|  | 		return value, position + 2 | ||||||
|  | 	elseif typeByte == 3 then -- int i32 | ||||||
|  | 		local value = readi32(b, position) | ||||||
|  | 		return value, position + 4 | ||||||
|  | 	elseif typeByte == 4 then -- f64 | ||||||
|  | 		local value = readf64(b, position) | ||||||
|  | 		return value, position + 8 | ||||||
|  | 	elseif typeByte == 5 then -- boolean | ||||||
|  | 		local value = readu8(b, position) == 1 | ||||||
|  | 		return value, position + 1 | ||||||
|  | 	elseif typeByte == 6 then -- string u8 | ||||||
|  | 		local length = readu8(b, position) | ||||||
|  | 		local value = readstring(b, position + 1, length) | ||||||
|  | 		return value, position + length + 1 | ||||||
|  | 	elseif typeByte == 7 then -- string u16 | ||||||
|  | 		local length = readu16(b, position) | ||||||
|  | 		local value = readstring(b, position + 2, length) | ||||||
|  | 		return value, position + length + 2 | ||||||
|  | 	elseif typeByte == 8 then -- string i32 | ||||||
|  | 		local length = readi32(b, position) | ||||||
|  | 		local value = readstring(b, position + 4, length) | ||||||
|  | 		return value, position + length + 4 | ||||||
|  | 	elseif typeByte == 9 then -- Vector3 | ||||||
|  | 		local x = readf32(b, position) | ||||||
|  | 		local y = readf32(b, position + 4) | ||||||
|  | 		local z = readf32(b, position + 8) | ||||||
|  | 		return Vector3.new(x, y, z), position + 12 | ||||||
|  | 	elseif typeByte == 10 then -- Vector2 | ||||||
|  | 		local x = readf32(b, position) | ||||||
|  | 		local y = readf32(b, position + 8) | ||||||
|  | 		return Vector2.new(x, y), position + 8 | ||||||
|  | 	elseif typeByte == 11 then -- CFrame | ||||||
|  | 		local components = {} | ||||||
|  | 		for i = 1, 12 do | ||||||
|  | 			table.insert(components, readf32(b, position + (i - 1) * 4)) | ||||||
|  | 		end | ||||||
|  | 		return CFrame.new(unpack(components)), position + 48 | ||||||
|  | 	elseif typeByte == 12 then -- Color3 | ||||||
|  | 		local r = readu8(b, position) | ||||||
|  | 		local g = readu8(b, position + 1) | ||||||
|  | 		local b = readu8(b, position + 2) | ||||||
|  | 		return Color3.fromRGB(r, g, b), position + 3 | ||||||
|  | 	end | ||||||
|  | 	error(`Unsupported type marker: {typeByte}`) | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | function Buffer.new(): Dedicated.DedicatedType | ||||||
| 	return Dedicated() | 	return Dedicated() | ||||||
| end | end | ||||||
| 
 | 
 | ||||||
|  | @ -21,4 +128,22 @@ function Buffer.revert(s: string): buffer | ||||||
| 	return fromstring(s) | 	return fromstring(s) | ||||||
| end | end | ||||||
| 
 | 
 | ||||||
|  | function Buffer.write(data: { any }): (buffer, (any)?) | ||||||
|  | 	local newBuffer = Dedicated() | ||||||
|  | 	newBuffer:pack(data) | ||||||
|  | 	return newBuffer:buildAndRemove() | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | function Buffer.read(b: buffer, ref: { any }?): any? | ||||||
|  | 	local position = 0 | ||||||
|  | 	local result = {} | ||||||
|  | 	while position < len(b) do | ||||||
|  | 		local value | ||||||
|  | 		value, position = readValue(b, position, ref) | ||||||
|  | 		table.insert(result, value) | ||||||
|  | 	end | ||||||
|  | 	ref = nil | ||||||
|  | 	return table.unpack(result) | ||||||
|  | end | ||||||
|  | 
 | ||||||
| return Buffer :: typeof(Buffer) | return Buffer :: typeof(Buffer) | ||||||
|  | @ -1,6 +1,5 @@ | ||||||
| --!strict | --!strict | ||||||
| --!native |  | ||||||
| --!optimize 2 | --!optimize 2 | ||||||
| return function(): number? | return function(): number? | ||||||
| 	return tonumber(string.sub(tostring(Random.new():NextNumber()), 3, 6)) -- 4 digits | 	return tonumber(string.sub(tostring(Random.new():NextNumber()), 3, 8)) -- 6 digits | ||||||
| end | end | ||||||
|  | @ -1,25 +1,54 @@ | ||||||
| --!strict | --!strict | ||||||
| --!native |  | ||||||
| --!optimize 2 | --!optimize 2 | ||||||
| local RateLimit = {} | local RateLimit = {} | ||||||
| 
 | 
 | ||||||
| local RunService = game:GetService("RunService") | local RunService = game:GetService("RunService") | ||||||
| local Assert = require(script.Parent.Assert) | local Assert = require(script.Parent.Assert) | ||||||
| local Event = require(script.Parent.Parent.Event).Reliable | local Events = require(script.Parent.Parent.Event) | ||||||
|  | local Reliable, Unreliable, Request = Events.Reliable, Events.Unreliable, Events.Request | ||||||
|  | local Signal = require(script.Parent.Parent.Signal)("Warp_OnSpamSignal") | ||||||
|  | 
 | ||||||
|  | local map, activity, meta = {}, {}, {} | ||||||
|  | setmetatable(meta , { | ||||||
|  | 	__index = map, | ||||||
|  | 	__newindex  = function(self, key, value) | ||||||
|  | 		if not activity[key] then | ||||||
|  | 			activity[key] = os.clock() | ||||||
|  | 		end | ||||||
|  | 		if (os.clock()-activity[key]) >= 1 then | ||||||
|  | 			activity[key] = os.clock() | ||||||
|  | 			map[key] = 1 | ||||||
|  | 			return | ||||||
|  | 		end | ||||||
|  | 		if value >= 1e2 then -- 100 | ||||||
|  | 			Signal:Fire(key) | ||||||
|  | 			return | ||||||
|  | 		end | ||||||
|  | 		map[key] = value | ||||||
|  | 	end, | ||||||
|  | }) | ||||||
|  | 
 | ||||||
|  | local function onReceived(player: Player) | ||||||
|  | 	if not meta[player] then | ||||||
|  | 		meta[player] = 1 | ||||||
|  | 		return | ||||||
|  | 	end | ||||||
|  | 	meta[player] += 1 | ||||||
|  | end | ||||||
| 
 | 
 | ||||||
| function RateLimit.create(Identifier: string, entrance: number?, interval: number?) | function RateLimit.create(Identifier: string, entrance: number?, interval: number?) | ||||||
| 	Assert(typeof(Identifier) == "string", "Identifier must a string type.") | 	Assert(typeof(Identifier) == "string", "Identifier must a string type.") | ||||||
| 	if RunService:IsServer() then | 	if RunService:IsServer() then | ||||||
| 		Assert(typeof(entrance) == "number", "entrance must a number type.") | 		Assert(typeof(entrance) == "number", "entrance must a number type.") | ||||||
| 		Assert(entrance :: number > 0, "entrance must above 0.") | 		Assert(entrance :: number > 0, "entrance must above 0.") | ||||||
| 		Event:SetAttribute(Identifier.."_ent", entrance) | 		Reliable:SetAttribute(Identifier.."_ent", entrance) | ||||||
| 		Event:SetAttribute(Identifier.."_int", interval) | 		Reliable:SetAttribute(Identifier.."_int", interval) | ||||||
| 	else | 	else | ||||||
| 		while (not Event:GetAttribute(Identifier.."_ent")) or (not Event:GetAttribute(Identifier.."_int")) do | 		while (not Reliable:GetAttribute(Identifier.."_ent")) or (not Reliable:GetAttribute(Identifier.."_int")) do | ||||||
| 			task.wait(0.5) | 			task.wait(0.1) | ||||||
| 		end | 		end | ||||||
| 		entrance = tonumber(Event:GetAttribute(Identifier.."_ent")) | 		entrance = tonumber(Reliable:GetAttribute(Identifier.."_ent")) | ||||||
| 		interval = tonumber(Event:GetAttribute(Identifier.."_int")) | 		interval = tonumber(Reliable:GetAttribute(Identifier.."_int")) | ||||||
| 	end | 	end | ||||||
| 	local entrances: number = 0 | 	local entrances: number = 0 | ||||||
| 	return function(incoming: number?): boolean | 	return function(incoming: number?): boolean | ||||||
|  | @ -33,4 +62,14 @@ function RateLimit.create(Identifier: string, entrance: number?, interval: numbe | ||||||
| 	end | 	end | ||||||
| end | end | ||||||
| 
 | 
 | ||||||
|  | function RateLimit.Protect() | ||||||
|  | 	if not RunService:IsServer() or Reliable:GetAttribute("Protected") or Unreliable:GetAttribute("Protected") or Request:GetAttribute("Protected") then return end | ||||||
|  | 	Reliable:SetAttribute("Protected", true) | ||||||
|  | 	Unreliable:SetAttribute("Protected", true) | ||||||
|  | 	Request:SetAttribute("Protected", true) | ||||||
|  | 	Reliable.OnServerEvent:Connect(onReceived) | ||||||
|  | 	Unreliable.OnServerEvent:Connect(onReceived) | ||||||
|  | 	Request.OnServerEvent:Connect(onReceived) | ||||||
|  | end | ||||||
|  | 
 | ||||||
| return RateLimit :: typeof(RateLimit) | return RateLimit :: typeof(RateLimit) | ||||||
|  | @ -1,18 +1,19 @@ | ||||||
| --!strict | --!strict | ||||||
| --!native |  | ||||||
| --!optimize 2 | --!optimize 2 | ||||||
|  | local SerDes = {} | ||||||
| local RunService = game:GetService("RunService") | local RunService = game:GetService("RunService") | ||||||
| local SerInt = 0 | local SerInt = 0 | ||||||
| 
 | 
 | ||||||
| local Event = require(script.Parent.Parent.Event).Reliable | local Event = require(script.Parent.Parent.Event).Reliable | ||||||
| local Assert = require(script.Parent.Assert) | local Assert = require(script.Parent.Assert) | ||||||
| 
 | 
 | ||||||
| return function(Identifier: string, timeout: number?): number | function SerDes.increment(Identifier: string, timeout: number?): number | ||||||
| 	Assert(typeof(Identifier) == "string", "Identifier must be a string type.") | 	Assert(typeof(Identifier) == "string", "Identifier must be a string type.") | ||||||
| 	Assert(SerInt < 255, "reached max 255 identifiers.") |  | ||||||
| 	if RunService:IsServer() then | 	if RunService:IsServer() then | ||||||
|  | 		Assert(SerInt < 255, "reached max 255 identifiers.") | ||||||
| 		if not Event:GetAttribute(Identifier) then | 		if not Event:GetAttribute(Identifier) then | ||||||
| 			SerInt += 1 | 			SerInt += 1 | ||||||
|  | 			Event:SetAttribute(`{SerInt}`, Identifier) | ||||||
| 			Event:SetAttribute(Identifier, SerInt) | 			Event:SetAttribute(Identifier, SerInt) | ||||||
| 			--Event:SetAttribute(Identifier, string.pack("I1", SerInt)) -- I1 -> 255 max, I2 -> ~ 6.5e4 max. (SerInt), removed/disabled for buffer migration. | 			--Event:SetAttribute(Identifier, string.pack("I1", SerInt)) -- I1 -> 255 max, I2 -> ~ 6.5e4 max. (SerInt), removed/disabled for buffer migration. | ||||||
| 		end | 		end | ||||||
|  | @ -20,11 +21,11 @@ return function(Identifier: string, timeout: number?): number | ||||||
| 		local yieldThread: thread = coroutine.running() | 		local yieldThread: thread = coroutine.running() | ||||||
| 		local cancel = task.delay(timeout or 10, function() -- yield cancelation (timerout) | 		local cancel = task.delay(timeout or 10, function() -- yield cancelation (timerout) | ||||||
| 			task.spawn(yieldThread, nil) | 			task.spawn(yieldThread, nil) | ||||||
| 			error(`Serdes: {Identifier} is taking too long to retrieve, seems like not replicated on server.`, 2) | 			error(`Serdes: {Identifier} is taking too long to retrieve, seems like it's not replicated on server.`, 2) | ||||||
| 		end) | 		end) | ||||||
| 		task.spawn(function() | 		task.spawn(function() | ||||||
| 			while coroutine.status(cancel) ~= "dead" and task.wait(0.5) do -- let it loop for yields! | 			while coroutine.status(cancel) ~= "dead" and task.wait(0.04) do -- let it loop for yields! 1/24 | ||||||
| 				if (Event:GetAttribute(Identifier)) then | 				if Event:GetAttribute(Identifier) then | ||||||
| 					task.cancel(cancel) | 					task.cancel(cancel) | ||||||
| 					task.spawn(yieldThread, Event:GetAttribute(Identifier)) | 					task.spawn(yieldThread, Event:GetAttribute(Identifier)) | ||||||
| 					break | 					break | ||||||
|  | @ -35,3 +36,15 @@ return function(Identifier: string, timeout: number?): number | ||||||
| 	end | 	end | ||||||
| 	return Event:GetAttribute(Identifier) | 	return Event:GetAttribute(Identifier) | ||||||
| end | end | ||||||
|  | 
 | ||||||
|  | function SerDes.decrement() | ||||||
|  | 	if not RunService:IsServer() or SerInt <= 0 then return end | ||||||
|  | 	local Identifier = Event:GetAttribute(`{SerInt}`) | ||||||
|  | 	if not Identifier then return end | ||||||
|  | 	Event:SetAttribute(`{Identifier}`, nil) | ||||||
|  | 	Event:SetAttribute(`{SerInt}`, nil) | ||||||
|  | 	SerInt -= 1 | ||||||
|  | 	Identifier = nil | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | return SerDes :: typeof(SerDes) | ||||||
|  | @ -1,30 +1,21 @@ | ||||||
| --!native | --!native | ||||||
| --!strict | --!strict | ||||||
| --!optimize 2 | --!optimize 2 | ||||||
| local thread: thread? = nil | local thread: thread? | ||||||
| 
 | 
 | ||||||
| local function passer(fn, ...): () | local function passer<T...>(func: (T...) -> (), ...: T...): () | ||||||
| 	local hold = thread | 	local HoldThread: thread = thread :: thread | ||||||
| 	thread = nil | 	thread = nil | ||||||
| 	fn(...) | 	func(...) | ||||||
| 	thread = hold | 	thread = HoldThread | ||||||
| end | end | ||||||
| 
 | 
 | ||||||
| local function yield(): never | local function newThread(): () | ||||||
| 	while true do | 	thread = coroutine.running() | ||||||
| 		passer(coroutine.yield()) | 	while true do passer(coroutine.yield()) end | ||||||
| 	end |  | ||||||
| end | end | ||||||
| 
 | 
 | ||||||
| if not thread then | return function<T...>(func: (T...) -> (), ...: T...): () | ||||||
| 	thread = coroutine.create(yield) | 	if not thread then task.spawn(newThread) end | ||||||
| 	coroutine.resume(thread :: any, thread) | 	task.spawn(thread :: thread, func, ...) | ||||||
| end |  | ||||||
| 
 |  | ||||||
| return function(fn: (...any) -> (...any?), ...: any): () |  | ||||||
| 	if not thread then |  | ||||||
| 		thread = coroutine.create(yield) |  | ||||||
| 		coroutine.resume(thread :: any, thread) |  | ||||||
| 	end |  | ||||||
| 	task.spawn(thread :: thread, fn, ...) |  | ||||||
| end | end | ||||||
|  | @ -32,32 +32,32 @@ function Index.Client(Identifier: string, conf: Type.ClientConf?): Type.Client | ||||||
| 	return require(Client.Index)(Identifier, conf) :: Type.Client | 	return require(Client.Index)(Identifier, conf) :: Type.Client | ||||||
| end | end | ||||||
| 
 | 
 | ||||||
| function Index.fromServerArray(arrays: { any }): Type.fromServerArray | function Index.fromServerArray(arrays: { string } | { [string]: Type.ServerConf }): Type.fromServerArray | ||||||
| 	Assert(IsServer, `[Warp]: Calling .fromServerArray({arrays}) on client side (expected server side)`) | 	Assert(IsServer, `[Warp]: Calling .fromServerArray({arrays}) on client side (expected server side)`) | ||||||
| 	Assert(typeof(arrays) == "table", "[Warp]: Array must be a table type, got {typeof(arrays)}") | 	Assert(typeof(arrays) == "table", "[Warp]: Array must be a table type, got {typeof(arrays)}") | ||||||
| 	local copy = {} | 	local copy: { [string]: Type.Server } = {} | ||||||
| 	for param1: any, param2: any in arrays do | 	for param1, param2: string | Type.ServerConf in arrays do | ||||||
| 		if typeof(param2) == "table" then | 		if typeof(param2) == "table" then | ||||||
| 			copy[param1] = Index.Server(param1, param2) | 			copy[param1] = Index.Server(param1, param2) | ||||||
| 		else | 		else | ||||||
| 			copy[param2] = Index.Server(param2) | 			copy[param2] = Index.Server(param2) | ||||||
| 		end | 		end | ||||||
| 	end | 	end | ||||||
| 	return copy :: typeof(copy) | 	return copy | ||||||
| end | end | ||||||
| 
 | 
 | ||||||
| function Index.fromClientArray(arrays: { any }): Type.fromClientArray | function Index.fromClientArray(arrays: { string } | { [string]: Type.ClientConf }): Type.fromClientArray | ||||||
| 	Assert(not IsServer, `[Warp]: Calling .fromClientArray({arrays}) on server side (expected client side)`) | 	Assert(not IsServer, `[Warp]: Calling .fromClientArray({arrays}) on server side (expected client side)`) | ||||||
| 	Assert(typeof(arrays) == "table", `[Warp]: Array must be a table type, got {typeof(arrays)}`) | 	Assert(typeof(arrays) == "table", `[Warp]: Array must be a table type, got {typeof(arrays)}`) | ||||||
| 	local copy = {} | 	local copy = {} | ||||||
| 	for param1: any, param2: any in arrays do | 	for param1, param2: string | Type.ClientConf in arrays do | ||||||
| 		if typeof(param2) == "table" then | 		if typeof(param2) == "table" then | ||||||
| 			copy[param1] = Index.Client(param1, param2) | 			copy[param1] = Index.Client(param1, param2) | ||||||
| 		else | 		else | ||||||
| 			copy[param2] = Index.Client(param2) | 			copy[param2] = Index.Client(param2) | ||||||
| 		end | 		end | ||||||
| 	end | 	end | ||||||
| 	return copy :: typeof(copy) | 	return copy | ||||||
| end | end | ||||||
| 
 | 
 | ||||||
| function Index.Signal(Identifier: string) | function Index.Signal(Identifier: string) | ||||||
|  |  | ||||||
|  | @ -1,5 +1,5 @@ | ||||||
| -- Warp Library (@Eternity_Devs) | -- Warp Library (@Eternity_Devs) | ||||||
| -- version 1.0.10 | -- version 1.0.14 | ||||||
| --!strict | --!strict | ||||||
| --!native | --!native | ||||||
| --!optimize 2 | --!optimize 2 | ||||||
|  | @ -11,6 +11,8 @@ return { | ||||||
| 	fromServerArray = Index.fromServerArray, | 	fromServerArray = Index.fromServerArray, | ||||||
| 	fromClientArray = Index.fromClientArray, | 	fromClientArray = Index.fromClientArray, | ||||||
| 	 | 	 | ||||||
|  | 	OnSpamSignal = Index.OnSpamSignal, | ||||||
|  | 
 | ||||||
| 	Signal = Index.Signal, | 	Signal = Index.Signal, | ||||||
| 	fromSignalArray = Index.fromSignalArray, | 	fromSignalArray = Index.fromSignalArray, | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,10 +1,10 @@ | ||||||
| [package] | [package] | ||||||
| name = "imezx/warp" | name = "imezx/warp" | ||||||
| version = "1.0.10" | version = "1.0.14" | ||||||
| registry = "https://github.com/UpliftGames/wally-index" | registry = "https://github.com/UpliftGames/wally-index" | ||||||
| realm = "shared" | realm = "shared" | ||||||
| license = "MIT" | license = "MIT" | ||||||
| exclude = ["node_modules", "docs", ".github", "*.rbxl", "*.rbxmx", "*.rbxml", "TestEZ", "test.project.json"] | exclude = ["node_modules", "docs", ".github", "*.rbxl", "*.rbxmx", "*.rbxml", "*.rbxm", "TestEZ", "test.project.json"] | ||||||
| description = "A very-fast & powerful networking library for Roblox." | description = "A very-fast & powerful networking library for Roblox." | ||||||
| 
 | 
 | ||||||
| [dependencies] | [dependencies] | ||||||
		Loading…
	
		Reference in a new issue