local function SendUDP(content) if content then helpers.send_udp(options.udpPort, content, options.senderPlayerIndex) else error("Missing udpPort or Content") end end ---Chunks content into appropriately sized pieces, respecting header lines ---Chunks content greedily to minimize UDP calls ---Fills 200KB buffer as completely as possible, including multiple headers if they fit ---Only prepends header when chunk starts mid-section ---@return table chunks Array of {header=string|nil, content=string}---@param content string ---@param maxSizekB number|nil Default 200KB local function ChunkContent(content, maxSizekB) local maxSize = (maxSizekB or 200) * 1024 local len = #content local chunks = {} -- 1. Pre-compute headers (O(N)) local headers = {} local hPos = 1 while true do local hStart, hEnd, hText = content:find("%%-%-%-(.-)%%-%-%-\n", hPos) if not hStart then break end table.insert(headers, {startPos = hStart, text = "---" .. hText .. "---"}) hPos = hEnd + 1 end local start = 1 local headerIdx = 1 local activeHeaderText = nil while start <= len do while headerIdx <= #headers and headers[headerIdx].startPos <= start do activeHeaderText = headers[headerIdx].text headerIdx = headerIdx + 1 end local isAtHeaderStart = (headerIdx > 1 and headers[headerIdx-1].startPos == start) local needsPrepend = (not isAtHeaderStart) and (activeHeaderText ~= nil) local overhead = needsPrepend and (#activeHeaderText + 1) or 0 local availableSpace = maxSize - overhead if availableSpace <= 0 then error("Header exceeds maxSize") end local target = math.min(start + availableSpace - 1, len) if target == len then table.insert(chunks, {header = needsPrepend and activeHeaderText or nil, content = content:sub(start, len)}) break end -- Optimized C-level search for newline local chunkView = content:sub(start, target) local lastNewline = chunkView:match(".*()\n") if not lastNewline then error("Line too long at byte " .. start) end local splitPos = start + lastNewline - 1 table.insert(chunks, {header = needsPrepend and activeHeaderText or nil, content = content:sub(start, splitPos)}) start = splitPos + 1 end return chunks end ---Sends content in chunks with optional header ---If content starts with header (------), it's automatically repeated in each chunk ---@param content string ---@param maxSizekB number|nil Default 200KB function SendChunked(content, maxSizekB) if not content or #content == 0 then error("Missing or empty content") end local maxSize = (maxSizekB or 200) * 1024 -- Content is smaller than maxSize, so send immediately if #content <= maxSize then SendUDP(content) return end local chunks = ChunkContent(content, maxSizekB) for _, chunk in ipairs(chunks) do if chunk.header then SendUDP(chunk.header .. "\n" .. chunk.content) else SendUDP(chunk.content) end end end