diff --git a/docs/url.md b/docs/url.md index fc6301b..786e036 100644 --- a/docs/url.md +++ b/docs/url.md @@ -4,38 +4,18 @@ Small utility with basic functions handling url querys and encoding/decoding url ```luau local Players = game:GetService("Players") +local safeteleport = require("safeteleport") local url = require("url") -local launch_data_start = "&launchdata=" -local start_len = #launch_data_start - -local function get_launch_data(player: Player): string? - local launch_data = player:GetJoinData().LaunchData - - if launch_data and #launch_data == 0 then - return nil - elseif not launch_data then - return nil - end - local decoded = url.decode(launch_data) - local start = string.find(decoded, launch_data_start, 1, true) - - if start then - -- url does not contain launchdata - return nil - end - local data_start = start + start_len - -- this shouldnt ever be nil unless roblox does some insane changes to deeplinks - local end_char_pos = string.find(data, "&", data_start, true) - - return string.sub(decoded, data_start, if end_char_pos then end_char_pos - 1 else #data) -end - Players.PlayerAdded:Connect(function(player) - local data = get_launch_data(player) + local data = url.launchdata(player) if data then - -- do stuff + local placeid = tonumber(string.match(data, "placeid=%d+")) + + if placeid then + safeteleport(placeid, player) + end end end) @@ -45,7 +25,7 @@ end) ### `encode` -Encodes the given string using [percent-encoding](https://en.wikipedia.org/wiki/Percent-encoding), so that reserved characters except for spaces properly encode with `%` and two hexadecimal characters. Spaces by defualt are encoded as `+`, but can be specified to be encoded as `%20` with the second arg. +Encodes the given string using [percent-encoding](https://en.wikipedia.org/wiki/Percent-encoding), so that reserved characters except for spaces properly encode with `%` and two hexadecimal characters. Spaces by defualt are encoded as `%20`, but can be specified to be encoded as `+` with the second arg. ```luau local content = "Je suis allé au cinéma." -- French for "I went to the movies" @@ -58,6 +38,9 @@ print(encoded) -- "Je+suis+all%C3%A9+au+cin%C3%A9ma." Decodes the given string, into how it originally was before it was [encoded](#encode) +> [!NOTE] +> If the spaces in the url are encoded as `+`, you need to specifiy this using the second arg for it to be properly decoded + ```luau local encoded = "Je%20suis%20all%C3%A9%20au%20cinema%2E" local decoded = url.decode(input) @@ -88,3 +71,19 @@ local tbl = url.read_query(query) print(tbl.meow, tbl.mrrp) -- "mrrp", "meow" ``` + +### `get_launchdata` + +Gets the launchdata for the given player (the actual launchdata not the full join url that [`Player:GetJoinData().LaunchData`](https://create.roblox.com/docs/reference/engine/classes/Player#GetJoinData) gives), and decodes it + +```luau +local Players = game:GetService("Players") + +Players.PlayerAdded:Connect(function(player) + local launchdata = url.get_launchdata(player) + + if launchdata then + print(`{player.Name}'s launchdata: {launchdata}`) + end +end) +``` \ No newline at end of file diff --git a/libs/Url/init.luau b/libs/Url/init.luau index eef1765..2ac7a0d 100644 --- a/libs/Url/init.luau +++ b/libs/Url/init.luau @@ -6,66 +6,78 @@ export type SpaceEncoding = "+" | "%20" --- these dont have fast calls -local string_format = string.format -local string_gmatch = string.gmatch -local string_gsub = string.gsub +local LAUNCH_DATA_START = "&launchdata=" +local START_LEN = #LAUNCH_DATA_START +local FORMAT = string.format +local GMATCH = string.gmatch +local MATCH = string.match +local GSUB = string.gsub -local function char_to_code(char: string): string - return string_format("%%%02X", string.byte(char)) +local function CHAR_TO_CODE(char: string): string + return FORMAT("%%%02X", string.byte(char)) end -local function code_to_char(code: string): string +local function CODE_TO_CHAR(code: string): string return string.char(tonumber(code, 16) :: any) end --- i just wanted to do this because i find it funny, --- sure its a bit less readable but its not thaaaat bad -local function encode_url(url: string, encode_spaces_as: SpaceEncoding?): string - return ( - string_gsub( - string_gsub( - string_gsub(url, "([^%w _%%%-%.~])", char_to_code), - "\n", "\r\n" - ), - "%s+", if encode_spaces_as == "%20" then "%%20" else "+" - ) - ) +-- modified from: +-- https://devforum.roblox.com/t/233570/2 +local function DECODE(url: string, spaces_encoded_as: SpaceEncoding?): string + local chars_decoded = GSUB(url, "%%(%x%x)", CODE_TO_CHAR) + + if spaces_encoded_as == "+" then + return (GSUB(chars_decoded, "+", " ")) + else + return chars_decoded + end end -local function queryify(tbl: { [string]: string }, encode_spaces_as: SpaceEncoding?): string - local query_strings = {} :: { string } +local function ENCODE(url: string, encode_spaces_as: SpaceEncoding?): string + local space_encode_char = if encode_spaces_as == "+" then "+" else "%%20" + local chars_encoded = (GSUB(url, "([^%w _%%%-%.~])", CHAR_TO_CODE)) + local new_line_encoded = (GSUB(chars_encoded, "\n", "\r\n")) - for key, value in tbl do - local encoded_value = encode_url(value, encode_spaces_as) - local encoded_key = encode_url(key, encode_spaces_as) - table.insert(query_strings, `{encoded_key}={encoded_value}`) - end - return `?{table.concat(query_strings, "&")}` + return (GSUB(new_line_encoded, "%s+", space_encode_char)) end --- modified from: --- https://devforum.roblox.com/t/233570/2 -local function decode_url(url: string): string - return (string_gsub( - string_gsub(url, "%%(%x%x)", code_to_char), - "+", " " - )) +local url = {} + +function url.get_launchdata(player: Player, launchdata_spaces_encoded_as: SpaceEncoding?): string? + local launch_data = player:GetJoinData().LaunchData + + if launch_data and #launch_data ~= 0 then + local decoded = DECODE(launch_data, launchdata_spaces_encoded_as) + -- pattern for getting a string with a non zero length + -- right after "&launchdata=" that doesn't include the & character + return MATCH(decoded, "&launchdata=([^&]+)") + else + return nil + end end -- modified from: https://stackoverflow.com/questions/28916182/parse-parameters-out-of-url-in-lua -local function read_query(url: string): { [string]: string } - local query_tbl = {} :: { [string]: string } +function url.read_query(url: string, spaces_encoded_as: SpaceEncoding?): { [string]: string } + local query_tbl = {} - for key, value: any in string_gmatch(decode_url(url), "([^&=?]-)=([^&=?]+)") do + for key, value: any in GMATCH(DECODE(url, spaces_encoded_as), "([^&=?]-)=([^&=?]+)") do query_tbl[key] = value end return query_tbl end -return table.freeze { - read_query = read_query, - queryify = queryify, - decode = decode_url, - encode = encode_url, -} +function url.queryify(t: { [string]: string }, encode_spaces_as: SpaceEncoding?): string + local query_strings = {} :: { string } + + for key, value in t do + local encoded_value = ENCODE(value, encode_spaces_as) + local encoded_key = ENCODE(key, encode_spaces_as) + table.insert(query_strings, `{encoded_key}={encoded_value}`) + end + return `?{table.concat(query_strings, "&")}` +end + +url.encode = ENCODE +url.decode = DECODE + +return table.freeze(url) diff --git a/libs/Url/test.luau b/libs/Url/test.luau index 4e608ea..fdc6719 100644 --- a/libs/Url/test.luau +++ b/libs/Url/test.luau @@ -5,11 +5,11 @@ local TEST, CASE, CHECK, FINISH = testkit.test() TEST("url", function() do CASE("encode %%20") - CHECK(url.encode("Je suis allé au cinéma.", "%20") == "Je%20suis%20all%C3%A9%20au%20cin%C3%A9ma.") + CHECK(url.encode("Je suis allé au cinéma.") == "Je%20suis%20all%C3%A9%20au%20cin%C3%A9ma.") end do CASE("encode +") - CHECK(url.encode("Je suis allé au cinéma.") == "Je+suis+all%C3%A9+au+cin%C3%A9ma.") + CHECK(url.encode("Je suis allé au cinéma.", "+") == "Je+suis+all%C3%A9+au+cin%C3%A9ma.") end do CASE("decode %%20") @@ -17,7 +17,7 @@ TEST("url", function() end do CASE("decode +") - CHECK(url.decode("Je+suis+all%C3%A9+au+cin%C3%A9ma.") == "Je suis allé au cinéma.") + CHECK(url.decode("Je+suis+all%C3%A9+au+cin%C3%A9ma.", "+") == "Je suis allé au cinéma.") end do CASE("queryify") @@ -35,4 +35,17 @@ TEST("url", function() CHECK(tbl.meow == "mrrp" and tbl.mrrp == "meow") end + + do CASE("get_launchdata") + local launchdata = "https://libs.luau.lol/lmao&meow=mrrp&mrrp=meow&launchdata=kitty catters unite now!" + local mock_player = table.freeze({ + GetJoinData = function() + return table.freeze({ + LaunchData = launchdata + }) + end + }) + + CHECK(url.get_launchdata(mock_player :: any) == "kitty catters unite now!") + end end) \ No newline at end of file