Dzsozi (h03) Posted November 12, 2022 Share Posted November 12, 2022 Hello! I would like to get the positions of all the lampposts. I was trying to use processLineOfSight to achieve this, since getElementsByType("object") doesn't return default world objects. I tried using a loop from 0 to 3000 on the x and y axis, but I get an infinite running script error. Why do I get this error and how could I achieve a scan on the world? Here's my current code: (I commented out most parts while trying to find the cause of the problem, so I would only see the positions of all the lamps if the scan was successful) local STREET_LIGHTS = { VALID_MODELS = { [1294] = {name = "mlamppost", offsets = {Vector3(0,0.3,1)}, radius = {start = 0.075, stop = 50}}, [1297] = {name = "lamppost1", offsets = {Vector3(0,0.3,1)}, radius = {start = 0.075, stop = 50}}, [1290] = {name = "lamppost2", offsets = {Vector3(3,0.03,5.4), Vector3(-3,0.03,5.4)}, radius = {start = 0.075, stop = 75}}, [1226] = {name = "lamppost3", offsets = {Vector3(-1.3,0.165,3.675)}, radius = {start = 0.075, stop = 50}}, }, OBJECT_LIGHTS = {}, } function processStreetLights() for x = 0, 3000, 5 do for y = 0, 3000, 5 do local hit, _,_,_,_,_,_,_,_,_,_,worldModelID,worldX,worldY,worldZ,worldRotX,worldRotY,worldRotZ,_ = processLineOfSight( x, y, 0, x + 5, y + 5, 500, true, false, false, true, false, false, false, false, nil, true, false ) if hit then --[[if STREET_LIGHTS.VALID_MODELS[worldModelID] then local data = STREET_LIGHTS.VALID_MODELS[worldModelID] local searchlights = {} for i, offset in pairs(data.offsets) do local startPos = Vector3(worldX, worldY, worldZ + offset.z) --Vector3(exports["sa_utility"]:getPositionFromElementOffset(obj, offset.x, offset.y, offset.z)) startPos.x = worldX + math.sin( math.rad( worldRotZ ) ) * offset.x startPos.y = worldY + math.cos( math.rad( worldRotZ ) ) * offset.y local endPos = startPos --Vector3(exports["sa_utility"]:getPositionFromElementOffset(obj, offset.x, offset.y, 0.25)) endPos.z = worldZ + 0.25 searchlights[i] = SearchLight(startPos, endPos, data.radius.start, data.radius.stop, true) end --STREET_LIGHTS.OBJECT_LIGHTS[obj] = searchlights table.insert(STREET_LIGHTS.OBJECT_LIGHTS, searchlights) end]] print(worldX, worldY, worldZ) end end end --[[for k, obj in pairs(getElementsByType("object")) do if STREET_LIGHTS.VALID_MODELS[obj.model] then local data = STREET_LIGHTS.VALID_MODELS[obj.model] local searchlights = {} for i, offset in pairs(data.offsets) do local startPos = Vector3(exports["sa_utility"]:getPositionFromElementOffset(obj, offset.x, offset.y, offset.z)) local endPos = Vector3(exports["sa_utility"]:getPositionFromElementOffset(obj, offset.x, offset.y, 0.25)) searchlights[i] = SearchLight(startPos, endPos, data.radius.start, data.radius.stop, true) end STREET_LIGHTS.OBJECT_LIGHTS[obj] = searchlights end end]] return true end addEventHandler("onClientResourceStart", resourceRoot, function() processStreetLights() return true end) Thank you for help in advance! Link to comment
Moderators IIYAMA Posted November 12, 2022 Moderators Share Posted November 12, 2022 6 hours ago, Dzsozi (h03) said: Why do I get this error and how could I achieve a scan on the world? There is a limit code can run for X amount of time each frame. If exceed this amount, the code will abort and you will receive this error. You can use a coroutine in order to temporary stop your code and resume in the next frame. local co co = coroutine.create(function () -- your code START ---------------------- -- inner loop START -- -- if getTickCount > ... then callNextFrame(function () coroutine.resume(co) end) coroutine.yield() -- -- inner loop END -- ---------------------- -- code END end) -- start coroutine.resume(co) Calling next frame: https://gitlab.com/IIYAMA12/mta-communication-enchantment/-/blob/master/sync/sync_shared.lua#L421 (note there are some variables that are defined above the function) Link to comment
Moderators IIYAMA Posted November 14, 2022 Moderators Share Posted November 14, 2022 Is everything clear or do you have any questions? @Dzsozi (h03) Link to comment
Scripting Moderators thisdp Posted November 15, 2022 Scripting Moderators Share Posted November 15, 2022 by the way, you need to move your camera at the same coordinate of processLineOfSight, because of the streaming system. Link to comment
Dzsozi (h03) Posted November 16, 2022 Author Share Posted November 16, 2022 On 14/11/2022 at 18:19, IIYAMA said: Is everything clear or do you have any questions? @Dzsozi (h03) Sorry I didn’t make time to reply, my fault. Actually it is not so clear, I don’t really know how to use coroutine functions, I never did before. Not so sure how to implement it for the purpose I need. On 15/11/2022 at 12:11, thisdp said: by the way, you need to move your camera at the same coordinate of processLineOfSight, because of the streaming system. Thank you for the advice I didn’t even think about that and actually makes sense to do it. Link to comment
Moderators IIYAMA Posted November 16, 2022 Moderators Share Posted November 16, 2022 1 hour ago, Dzsozi (h03) said: . Actually it is not so clear, I don’t really know how to use coroutine functions A coroutine is a kind of thread type, which you can use to put a function to a temporary stop. The coroutine.create method requires a function and returns the coroutine. local co co = coroutine.create(function () end) The coroutine.yield method stops the thread, when it is called inside of the function. local co co = coroutine.create(function () coroutine.yield() end) To start a coroutine you can must call the method coroutine.resume, the first argument has to be the coroutine. local co co = coroutine.create(function () print("A") coroutine.yield() print("B") end) coroutine.resume(co) -- print A -- but does NOT print B To resume the coroutine after yield, just call coroutine.resume again. local co co = coroutine.create(function () coroutine.yield() print("B") end) coroutine.resume(co) -- start -- coroutine.yield() coroutine.resume(co) -- print B Add an async function of any kind (timer) and you are ready to go. local co co = coroutine.create(function () print("A") coroutine.yield() print("B") end) coroutine.resume(co) -- print A setTimer(function () coroutine.resume(co) -- print B end, 5000, 1) 1 Link to comment
Dzsozi (h03) Posted November 17, 2022 Author Share Posted November 17, 2022 (edited) On 16/11/2022 at 19:36, IIYAMA said: A coroutine is a kind of thread type, which you can use to put a function to a temporary stop. Thank you for teaching me, really, I tried to make the scanning with these functions, I get an error spam "cannot resume dead coroutine" because of the timer (this line) STREET_LIGHTS.SCAN_TIMER = setTimer(resumeScan, 1000, 0) Here's my current code, could you help me with what am I doing wrong? local STREET_LIGHTS = { VALID_MODELS = { [1294] = {name = "mlamppost", offsets = {Vector3(0,0.3,1)}, radius = {start = 0.075, stop = 50}}, [1297] = {name = "lamppost1", offsets = {Vector3(0,0.3,1)}, radius = {start = 0.075, stop = 50}}, [1290] = {name = "lamppost2", offsets = {Vector3(3,0.03,5.4), Vector3(-3,0.03,5.4)}, radius = {start = 0.075, stop = 75}}, [1226] = {name = "lamppost3", offsets = {Vector3(-1.3,0.165,3.675)}, radius = {start = 0.075, stop = 50}}, }, OBJECT_LIGHTS = {}, } local SCAN = { current = Vector2(-3000, -3000), } local SCAN_WORLD = coroutine.create(function() for x = SCAN.current.x, 3000, 10 do if math.abs(x) % 100 == 100 then SCAN.current.x = x print"yield" coroutine.yield() end for y = SCAN.current.y, 3000, 10 do if SCAN.current.x >= 3000 and SCAN.current.y >= 3000 then if isTimer(STREET_LIGHTS.SCAN_TIMER) then killTimer(STREET_LIGHTS.SCAN_TIMER) end STREET_LIGHTS.SCAN_TIMER = nil setCameraTarget(localPlayer) coroutine.yield() break end if math.abs(y) % 100 == 100 then SCAN.current.y = y coroutine.yield() end setCameraMatrix(x, y, 500, x, y, 0) local hit, _,_,_,_,_,_,_,_,_,_,worldModelID,worldX,worldY,worldZ,worldRotX,worldRotY,worldRotZ,_ = processLineOfSight( x, y, 500, x + 5, y + 5, 0, true, false, false, true, false, false, false, false, nil, true, false ) if hit then --if STREET_LIGHTS.VALID_MODELS[worldModelID] then print(worldModelID) -- do i actually get the models on screen? i get some numbers for sure but camera is stuck and position is not updating --end end end end return true end) local function resumeScan() return coroutine.resume(SCAN_WORLD) end addEventHandler("onClientResourceStart", resourceRoot, function() if not isTimer(STREET_LIGHTS.SCAN_TIMER) then STREET_LIGHTS.SCAN_TIMER = setTimer(resumeScan, 1000, 0) end return true end) And actually I don't even get the "yield" output as expected from this line: if math.abs(x) % 100 == 100 then SCAN.current.x = x print"yield" coroutine.yield() end Camera is stuck at one place on the top right corner of the map and first I get a bunch of spam of 3 or 4 numbers, then the error about cannot resume dead coroutine. Edited November 17, 2022 by Dzsozi (h03) Link to comment
Moderators IIYAMA Posted November 17, 2022 Moderators Share Posted November 17, 2022 54 minutes ago, Dzsozi (h03) said: Camera is stuck at one place on the top right corner of the map and first I get a bunch of spam of 3 or 4 numbers, then the error about cannot resume dead coroutine. A dead coroutine means that this: local SCAN_WORLD = coroutine.create(function() end) function has ended it's execution in some way. Either by an error or just running until the very end. I recommend to start the next async call just before the yield. This will eliminate the problems of resuming dead coroutines. So: STREET_LIGHTS.SCAN_TIMER = setTimer(resumeScan, 1000, 0) addEventHandler("onClientResourceStart", resourceRoot, function() if not isTimer(STREET_LIGHTS.SCAN_TIMER) then STREET_LIGHTS.SCAN_TIMER = setTimer(resumeScan, 1000, 1) end return true end) Run the code for X amount of time and start the resume timer there: (see a version of your current method below) local MAX_EXECUTION_INTERVAL = 3 -- ms local maxExecutionTime = 0 -- inside of the loop if getTickCount() > maxExecutionTime then STREET_LIGHTS.SCAN_TIMER = setTimer(resumeScan, 1000, 1) coroutine.yield() end You might consider balance the STREET_LIGHTS.SCAN_TIMER timer out with the max execution time. function resumeScan() maxExecutionTime = getTickCount() + MAX_EXECUTION_INTERVAL return coroutine.resume(SCAN_WORLD) end ---> 54 minutes ago, Dzsozi (h03) said: if math.abs(x) % 100 == 100 then SCAN.current.x = x print"yield" coroutine.yield() end math.abs(x) % 100 == 100 This will never be true 100 % 100 = 0 99 % 100 = 99 101 % 100 = 1 500 % 100 = 0 You might consider using 0 math.abs(x) % 100 == 0 if math.abs(x) % 100 == 0 then SCAN.current.x = x print"yield" STREET_LIGHTS.SCAN_TIMER = setTimer(resumeScan, 1000, 1) coroutine.yield() end 1 Link to comment
Dzsozi (h03) Posted November 23, 2022 Author Share Posted November 23, 2022 Sorry for bumping this topic that late, I got some free time now, I still wasn’t able to finish this project based on the examples, also I just realised that the scan will be going diagonally if I add to x and y at the same time, therefore the scan wouldn’t apply to the whole world. I would like to achieve an effect of scanning such as from the top left corner of the map to the bottom right, going sideways, and when the camera reaches the right side, it snaps back to the left and gets offset on the y axis. I am not sure about how to achieve this between the range of -3000 to 3000 on the x and y axis inside a coroutine function with loops, am I even doing it the right way by using for loop? I would have to prevent the loop from happening on the y axis before the scanX reaches the right side. Right now I can’t send my current code and I can’t remember what errors I got, I’m at work but I wanted to reply and let you know that I’m still interested and trying to fix & get it done. I will post my code asap, could you please maybe help me finishing and correcting it? Link to comment
Discord Moderators Zango Posted November 23, 2022 Discord Moderators Share Posted November 23, 2022 I read your original post and you wanted position of lamp posts. It would be a lot simpler to get this data from the .ipl files which contain placement of world models. These files are in the data folder of gta sa. 1 Link to comment
Dzsozi (h03) Posted November 24, 2022 Author Share Posted November 24, 2022 This is a great idea and would be much simpler and easier indeed, now that you said I realised it. But I think the reason I wanted to do a scanning type solution is because of the custom placed objects (i know I can check those and I already did in the original script), but also, more likely because of the removed world models. What would you suggest to check that, if I were doing the solution you mentioned? I have some custom maps which for sure remove some lamps, I made some that removes even a whole street. Link to comment
Discord Moderators Zango Posted November 28, 2022 Discord Moderators Share Posted November 28, 2022 3 solutions If you are removing the models from a script 1. Have the scripts call a central resource to remove the models. If they are removing the lamp post ID, store the position and radius in a table. 2. Manually store the position and radius in a table. If you are removing the models with map editor (.map files) 3. getElementsByType("removeWorldObject") will return custom elements that represent each removal. Use getElementData on each element and get posX, posY, posZ and radius data fields, on the ones with the right model. Once you have the x, y, z and radius of the world removals, you can skip IPL entries of your lamp post that contain a position within this sphere (defined by x, y,z and radius). To determine if a point is within a sphere, it's a simple distance check x1, y1, z1 x2, y2, z2 radius distance = math.sqrt((x2-x1)^2 + (y2-y1)^2 + (z2-z1)^2) if distance < radius then -- Point is within sphere else -- Point is outside end 1 Link to comment
Dzsozi (h03) Posted December 1, 2022 Author Share Posted December 1, 2022 On 28/11/2022 at 07:43, Zango said: 3. getElementsByType("removeWorldObject") will return custom elements that represent each removal. Use getElementData on each element and get posX, posY, posZ and radius data fields, on the ones with the right model. Thank you, I didn't know I could do this with getElementsByType Link to comment
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now