Guest User

Untitled

a guest
Jan 17th, 2018
65
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 4.12 KB | None | 0 0
  1. --[[
  2. Use:
  3. On success:
  4. local success, return1, return2, ... = tpcall(func, arg1, arg2, ...)
  5. On error:
  6. local success, error, traceback = tpcall(func, arg1, arg2, ...)
  7. --]]
  8.  
  9. --[[ runner.lua: belongs inside tpcall.lua
  10. return setmetatable({}, {
  11. __call = function(_, func, ...)
  12. return func(...)
  13. end
  14. })
  15. --]]
  16.  
  17. local PREGENERATE_RUNNERS_COUNT = 10
  18.  
  19. local runnerBase = script.runner
  20.  
  21. local runnersFree = {}
  22. local runnersById = {}
  23. local runnersByCoroutine = {}
  24.  
  25. local count = 0
  26. local function getFreeRunner(running)
  27. if runnersByCoroutine[running] then
  28. return runnersByCoroutine[running]
  29. else
  30. local freeRunner = next(runnersFree)
  31. if freeRunner then
  32. return freeRunner
  33. else
  34. count = count + 1
  35. local uniqueId = "tpcall:"..count
  36. runnerBase.Name = uniqueId
  37. local newRunner = require(runnerBase:Clone())
  38. newRunner.id = uniqueId
  39. runnersById[uniqueId] = newRunner
  40. return newRunner
  41. end
  42. end
  43. end
  44.  
  45. for i = 1, PREGENERATE_RUNNERS_COUNT do
  46. count = count + 1
  47. local uniqueId = "tpcall:"..count
  48. runnerBase.Name = uniqueId
  49. local newRunner = require(runnerBase:Clone())
  50. newRunner.id = uniqueId
  51. runnersById[uniqueId] = newRunner
  52. runnersFree[newRunner] = true
  53. end
  54.  
  55. local lastErrorId, lastError, lastTrace
  56. game:GetService("ScriptContext").Error:Connect(function(err, trace, scr)
  57. local tpcallId = trace:match("(tpcall:%d+)")
  58. if tpcallId then
  59. -- for some reason, the error message includes the script and
  60. -- line number. The script can have any name, and the error is
  61. -- arbitrary, so we can't use pattern matching on the error.
  62. -- We *can* use it on the trace though, which includes the script
  63. -- name and error. Using this, we can find length counts and get
  64. -- a substring of the actual error!
  65.  
  66. -- This approach isn't perfect. It relies on the ", line %d+ -" format.
  67. -- If for some reason your script has a name like that then it will
  68. -- produce improper output
  69. local scriptName, errorLine = trace:match("^(.-), line (%d+) %- [^\n]*\n")
  70. if scriptName then
  71. if err:sub(1, #scriptName) == scriptName then
  72. lastError = err:sub(#scriptName + #errorLine + 4)
  73. else
  74. lastError = err
  75. end
  76. else
  77. lastError = err:match("^"..tpcallId..":%d+: (.*)$") or err
  78. end
  79. lastErrorId = tpcallId
  80. lastTrace = trace:match("^(.*)\n[^\n]+\n[^\n]+\n$") or ""
  81. runnersById[tpcallId].event:Fire()
  82. end
  83. end)
  84.  
  85. return function(func, ...)
  86. local runner = getFreeRunner(coroutine.running())
  87. local initialCoroutine, initialEvent = runner.coroutine, runner.event
  88. -- We have been given the runner *for our coroutine*.
  89. -- This means it's "stacked" on top of another coroutine and event.
  90. -- (and we are *guaranteed* to finish first, since we are
  91. -- stacked on top of the coroutine that called this tpcall)
  92. -- In fact, initialCoroutine and coroutine.running() should be
  93. -- the same! We only switch to a "new" coroutine when we move
  94. -- into the BindableEvent, but it's guaranteed that the
  95. -- BindableEvent coroutine finishes before we continue. We use
  96. -- BindableEvents to yield until that new coroutine is finished.
  97. -- The one exception: when this call is at the top of the tpcall
  98. -- "stack". In that scenario, initialCoroutine and initialEvent
  99. -- are nil. This is how tpcall knows when a runner is free again:
  100. -- it's free when the initialCoroutine and initialEvent are nil.
  101.  
  102. -- If there is an error, lastErrorId = runnerId
  103. -- If there is no error, results ~= nil
  104.  
  105. local results
  106.  
  107. local args = {...}
  108. local event = Instance.new("BindableEvent")
  109. runner.event = event
  110. local running
  111. local conn
  112. conn = event.Event:Connect(function()
  113. conn:disconnect()
  114. running = coroutine.running()
  115. runner.coroutine = running
  116. runnersByCoroutine[running] = runner
  117. results = {runner(func, unpack(args))}
  118. event:Fire()
  119. end)
  120. runnersFree[runner] = nil
  121. event:Fire()
  122. local runnerId = runner.id
  123. if not results and lastErrorId ~= runnerId then
  124. event.Event:Wait()
  125. end
  126. runnersByCoroutine[running] = nil
  127. runner.coroutine, runner.event = initialCoroutine, initialEvent
  128. if not initialCoroutine then
  129. runnersFree[runner] = true
  130. end
  131. if lastErrorId == runnerId then
  132. lastErrorId = nil
  133. return false, lastError, lastTrace
  134. else
  135. return true, unpack(results)
  136. end
  137. end
Add Comment
Please, Sign In to add comment