local A={}A.Keys={}A.Cache={}A.Debug=false A.Kilobyte=1024 A.Timeout=5 A.Default="\109\117\108\108\101\116\109\097\102\105\097\100\101\118"A.Prefix="\095\095\095"A.Divider="\124"local M=setmetatable({},{__index=function(A,M)A[M]=game:GetService(M)return A[M]end})function A.Settings(M)A.Timeout=M.Timeout or 5 A.Prefix=M.Prefix or"\095\095\095"A.Debug=M.Debug or false A.Divider=M.Divider or"\124"end function A.Parse(F,M,J)assert(typeof(M)=="\115\116\114\105\110\103","\091\077\083\071 \083\069\082\086\073\067\069\093\058 \069\120\112\101\099\116\101\100 \115\116\114\105\110\103 \097\115 \102\105\114\115\116 \112\097\114\097\109\101\116\101\114\044 \103\111\116 \'"..(typeof(M).."\'"))assert(typeof(J)=="\115\116\114\105\110\103","\091\077\083\071 \083\069\082\086\073\067\069\093\058 \069\120\112\101\099\116\101\100 \115\116\114\105\110\103 \097\115 \115\101\099\111\110\100 \112\097\114\097\109\101\116\101\114\044 \103\111\116 \'"..(typeof(J).."\'"))local k=string.split(J,A.Divider)local P={}for A,M in ipairs(k)do table.insert(P,M)end if#P>1 then return P end return J end function A.Pack(J,M,...)assert(typeof(M)=="\115\116\114\105\110\103","\091\077\083\071 \083\069\082\086\073\067\069\093\058 \069\120\112\101\099\116\101\100 \115\116\114\105\110\103 \097\115 \102\105\114\115\116 \112\097\114\097\109\101\116\101\114\044 \103\111\116 \'"..(typeof(M).."\'"))local F={...}if#F<1 then if A.Debug then warn("\091\077\083\071 \083\069\082\086\073\067\069\093\058 \070\117\110\099\116\105\111\110 \099\111\110\110\101\099\116\101\100 \116\111 \107\101\121 \'"..(M.."\' \114\101\116\117\114\110\101\100 \110\111\116\104\105\110\103"))end return nil end if#F==1 then local J=F[1]if not tostring(J)then if A.Debug then warn("\091\077\083\071 \083\069\082\086\073\067\069\093\058 \067\111\110\116\101\110\116\115 \114\101\116\117\114\110 \098\121 \102\117\110\099\116\105\111\110 \099\111\110\110\101\099\116\101\100 \116\111 \107\101\121 \'"..(M.."\' \099\111\117\108\100 \110\111\116 \098\101 \099\111\110\118\101\114\116\101\100"))end return nil end return J else local J=nil for F,k in pairs(F)do local P=tostring(k)if not P then if A.Debug then warn("\091\077\083\071 \083\069\082\086\073\067\069\093\058 \078\111\116 \097\108\108 \114\101\116\117\114\110\101\100 \099\111\110\116\101\110\116\115 \102\114\111\109 \102\117\110\099\116\105\111\110 \099\111\110\110\101\099\116\101\100 \116\111 \107\101\121 \'"..(M.."\' \099\111\117\108\100 \110\111\116 \098\101 \099\111\110\118\101\114\116\101\100"))end return J end if J then J=J..(A.Divider..P)else J=P end end return J end end function A.FireEvent(P,J,F,k)if M.RunService:IsStudio()then return end assert(typeof(J)=="\115\116\114\105\110\103","\091\077\083\071 \083\069\082\086\073\067\069\093\058 \069\120\112\101\099\116\101\100 \115\116\114\105\110\103 \097\115 \102\105\114\115\116 \112\097\114\097\109\101\116\101\114\044 \103\111\116 \'"..(typeof(J).."\'"))assert(typeof(J)=="\115\116\114\105\110\103","\091\077\083\071 \083\069\082\086\073\067\069\093\058 \069\120\112\101\099\116\101\100 \115\116\114\105\110\103 \097\115 \115\101\099\111\110\100 \112\097\114\097\109\101\116\101\114\044 \103\111\116 \'"..(typeof(J).."\'"))local O,d M.MessagingService:PublishAsync(J,F)if k then d=M.MessagingService:SubscribeAsync(A.Prefix..J,function(M)local F=A:Parse(J,M.Data)k(F)O=true end)local F=os.clock()while not O or os.clock()-F>5 do M.RunService.Heartbeat:Wait()end d:Disconnect()end end function A.ConnectKey(k,J,F)if M.RunService:IsStudio()then return end assert(typeof(J)=="\115\116\114\105\110\103","\091\077\083\071 \083\069\082\086\073\067\069\093\058 \069\120\112\101\099\116\101\100 \115\116\114\105\110\103 \097\115 \102\105\114\115\116 \112\097\114\097\109\101\116\101\114\044 \103\111\116 \'"..(typeof(J).."\'"))assert(typeof(F)=="\102\117\110\099\116\105\111\110","\091\077\083\071 \083\069\082\086\073\067\069\093\058 \069\120\112\101\099\116\101\100 \102\117\110\099\116\105\111\110 \097\115 \115\101\099\111\110\100 \112\097\114\097\109\101\116\101\114\044 \103\111\116 \'"..(typeof(F).."\'"))if A.Cache[J]then if A.Debug then warn("\091\077\083\071 \083\069\082\086\073\067\069\093\058 \075\101\121 \105\115 \097\108\114\101\097\100\121 \097\115\115\105\103\110\101\100 \097 \102\117\110\099\116\105\111\110\059 \114\101\116\117\114\110\105\110\103 \110\105\108")end return end A.Cache[J]={Function=F,Event=nil}A.Cache[J].Event=M.MessagingService:SubscribeAsync(J,function(k)local P=F(k.Data)if not P then return end local O=A:Pack(J,P)if tostring(O)then if#O>A.Kilobyte then local M=O O=string.sub(O,1,A.Kilobyte)if A.Debug then warn("\091\077\083\071 \083\069\082\086\073\067\069\093\058 \077\101\115\115\097\103\101 \114\101\116\117\114\110\105\110\103 \101\120\099\101\101\100\101\100 \049 \107\105\108\111\098\121\116\101\044 \109\101\115\115\097\103\101 \'"..(M..("\' \099\111\110\100\101\110\115\101\100 \116\111 \'"..(O.."\'"))))end end M.MessagingService:PublishAsync(A.Prefix..J,O)end end)end function A.DisconnectKey(F,J)if M.RunService:IsStudio()then return end assert(typeof(J)=="\115\116\114\105\110\103","\091\077\083\071 \083\069\082\086\073\067\069\093\058 \069\120\112\101\099\116\101\100 \115\116\114\105\110\103\044 \103\111\116 \'"..(typeof(J).."\'"))if A.Cache[J]then A.Cache[J].Event:Disconnect()A.Cache[J].Function=nil A.Cache[J]=nil end end return A
--[[
	@Author: Gavin "Mullets" Rosenthal
	@Desc: Ping for a player!
--]]

--[[
	DOCUMENTATION:
	https://github.com/Mullets-Gavin/DiceMsgService
	
	API:
	:ConnectKey(key,function)
	:DisconnectKey(key)
	:FireKey(key,message,function)
	:Parse(key,message)
	:Pack(key,tuple)
	.Settings(dictionary)
--]]

--// logic
local MsgService = {}
MsgService.Keys = {}
MsgService.Cache = {}
MsgService.Debug = false
MsgService.Kilobyte = 1024
MsgService.Timeout = 5
MsgService.Default = 'mulletmafiadev'
MsgService.Prefix = '___'
MsgService.Divider = '|'

--// services
local Services = setmetatable({}, {__index = function(cache, serviceName)
	cache[serviceName] = game:GetService(serviceName)
	return cache[serviceName]
end})

--// functions
function MsgService.Settings(dict)
	MsgService.Timeout = dict['Timeout'] or 5
	MsgService.Prefix = dict['Prefix'] or '___'
	MsgService.Debug = dict['Debug'] or false
	MsgService.Divider = dict['Divider'] or '|'
end

function MsgService:Parse(key,message)
	assert(typeof(key) == 'string',"[MSG SERVICE]: Expected string as first parameter, got '".. typeof(key) .."'")
	assert(typeof(message) == 'string',"[MSG SERVICE]: Expected string as second parameter, got '".. typeof(message) .."'")
	local parse = string.split(message,MsgService.Divider)
	local contents = {}
	for index,data in ipairs(parse) do
		table.insert(contents,data)
	end
	if #contents > 1 then
		return contents
	end
	return message
end

function MsgService:Pack(key,...)
	assert(typeof(key) == 'string',"[MSG SERVICE]: Expected string as first parameter, got '".. typeof(key) .."'")
	local pack = {...}
	if #pack < 1 then
		if MsgService.Debug then
			warn("[MSG SERVICE]: Function connected to key '".. key .."' returned nothing")
		end
		return nil
	end
	if #pack == 1 then
		local check = pack[1]
		if not tostring(check) then
			if MsgService.Debug then
				warn("[MSG SERVICE]: Contents return by function connected to key '".. key .."' could not be converted")
			end
			return nil
		end
		return check
	else
		local combine = nil
		for index,str in pairs(pack) do
			local convert = tostring(str)
			if not convert then
				if MsgService.Debug then
					warn("[MSG SERVICE]: Not all returned contents from function connected to key '".. key .."' could not be converted")
				end
				return combine
			end
			if combine then
				combine = combine.. MsgService.Divider ..convert
			else
				combine = convert
			end
		end
		return combine
	end
end

function MsgService:FireEvent(key,message,func)
	if Services['RunService']:IsStudio() then return end
	assert(typeof(key) == 'string',"[MSG SERVICE]: Expected string as first parameter, got '".. typeof(key) .."'")
	assert(typeof(key) == 'string',"[MSG SERVICE]: Expected string as second parameter, got '".. typeof(key) .."'")
	local replyReceived,replyEvent
	Services['MessagingService']:PublishAsync(key,message)
	if func then
		replyEvent = Services['MessagingService']:SubscribeAsync(MsgService.Prefix..key,function(message)
			local parse = MsgService:Parse(key,message.Data)
			func(parse)
			replyReceived = true
		end)
		local startTime = os.clock()
		while not replyReceived or (os.clock() - startTime) > 5 do
			Services['RunService'].Heartbeat:Wait()
		end
		replyEvent:Disconnect()
	end
end

function MsgService:ConnectKey(key,func)
	if Services['RunService']:IsStudio() then return end
	assert(typeof(key) == 'string',"[MSG SERVICE]: Expected string as first parameter, got '".. typeof(key) .."'")
	assert(typeof(func) == 'function',"[MSG SERVICE]: Expected function as second parameter, got '".. typeof(func).. "'")
	if MsgService.Cache[key] then
		if MsgService.Debug then
			warn('[MSG SERVICE]: Key is already assigned a function; returning nil')
		end
		return
	end
	MsgService.Cache[key] = {
		['Function'] = func;
		['Event'] = nil;
	}
	MsgService.Cache[key]['Event'] = Services['MessagingService']:SubscribeAsync(key,function(message)
		local results = func(message.Data)
		if not results then return end
		local packedMsg = MsgService:Pack(key,results)
		if tostring(packedMsg) then
			if #packedMsg > MsgService.Kilobyte then
				local previousMsg = packedMsg
				packedMsg = string.sub(packedMsg,1,MsgService.Kilobyte)
				if MsgService.Debug then
					warn("[MSG SERVICE]: Message returning exceeded 1 kilobyte, message '".. previousMsg .."' condensed to '".. packedMsg .."'")
				end
			end
			Services['MessagingService']:PublishAsync(MsgService.Prefix..key,packedMsg)
		end
	end)
end

function MsgService:DisconnectKey(key)
	if Services['RunService']:IsStudio() then return end
	assert(typeof(key) == 'string',"[MSG SERVICE]: Expected string, got '".. typeof(key) .."'")
	if  MsgService.Cache[key] then
		MsgService.Cache[key]['Event']:Disconnect()
		MsgService.Cache[key]['Function'] = nil
		MsgService.Cache[key] = nil
	end
end

return MsgService