Add per-element, per-model and per-type stream distance functions - #5149
Add per-element, per-model and per-type stream distance functions#5149QueryOfficial wants to merge 2 commits into
Conversation
- Introduced methods for setting and getting stream distances for both element types and specific models. - Implemented a mechanism to resolve stream distances based on element type, model, and custom settings. - Enhanced the CClientManager to iterate over stream elements and refresh their distances when changes occur. - Updated CClientStreamElement to apply and refresh stream distances, ensuring proper handling of custom distances. - Added Lua bindings for stream distance functions, allowing script access to set and retrieve stream distances. This update improves the streaming system's flexibility and performance, ensuring elements are managed more effectively based on their distance from the camera.
| static const std::unordered_map<std::string, eClientEntityType> streamableTypes{ | ||
| {"object", CCLIENTOBJECT}, {"vehicle", CCLIENTVEHICLE}, {"ped", CCLIENTPED}, | ||
| {"player", CCLIENTPLAYER}, {"pickup", CCLIENTPICKUP}, {"marker", CCLIENTMARKER}, | ||
| }; |
There was a problem hiding this comment.
What about other types like building, sound, weapon or searchlight? From what I can see, CClientPointLight doesn't handle streaming.
There was a problem hiding this comment.
Added weapon and searchlight.
CClientWeapon derives from CClientObject and uses the object streamer, but reports CCLIENTWEAPON, so neither the object type override nor a model override was reaching it. It also needs its own RefreshStreamDistance() call, because the base constructor resolves the range while the dynamic type is still CCLIENTOBJECT. CClientSearchLight is a stream element on the light streamer, but the bulk refresh never visited it — it now iterates CClientPointLightsManager::m_SearchLightList.
The rest don't go through CClientStreamer at all, so there's nothing to expose:
building—CClientBuildingis a plainCClientEntity, GTA's building pool handles itsound—CClientSoundis a plainCClientEntitywith its own 3D distance modellight— right,CClientPointLightsisn't a stream element either
So the list is now object, vehicle, ped, player, pickup, marker, weapon, searchlight. I left a comment on the type table explaining why the others aren't there.
Both go through CClientStreamer but were missing from the type table. CClientWeapon is CClientObject derived with its own CCLIENTWEAPON type, so neither "object" nor a model override reached it, and it needs an extra RefreshStreamDistance() because the base constructor resolves it as an object. CClientSearchLight was never visited by the bulk refresh. Buildings, sounds and lights are plain CClientEntity and are not streamed by CClientStreamer, so they cannot be supported here.
Summary
Adds client-side Lua functions to override the streaming radius per element, per model and per element type, instead of every streamed element being tied to its streamer's fixed radius (markers 600, objects 500, low-LOD objects 1700, pickups 100, peds/players 250, vehicles 250, lights 600).
OOP:
Element:setStreamDistance(),Element:getStreamDistance(),element.streamDistance.The effective range is resolved as element > model > element type > streamer default. Accepted range is 1–3000; supported element types are
object,vehicle,ped,player,pickup,marker. Everything is client-only — no RPC, bitstream or server changes.Implementation notes:
CClientStreamElementcaches the resolved distance plus its squared / inverse-squared forms, becauseCClientStreamer::Restreamreads them for every active element every frame. The stream-out check now uses a per-element threshold, so the existing+50hysteresis applies to custom ranges too.m_fExpDistancekeeps its world-unit meaning becauseCNametagsreads it directly. The swap hysteresis was moved into that same ratio space and is derived from the streamer default, which reproduces the previous behaviour exactly for elements without a custom distance.Restream. Rather than widening that window for everyone (cost scales with map density), only elements that ask for a longer range are pinned into the active list. With the feature unused, behaviour is unchanged.CClientModelCacheManagerhadPED_STREAM_IN_DISTANCE/VEHICLE_STREAM_IN_DISTANCEhardcoded to 250. It now follows the streamers' actual largest range, otherwise raising ped/vehicle distance would skip model pre-caching and reintroduce blocking loads. The duplicated+50constant is nowCClientStreamer::STREAM_OUT_EXTRA_DISTANCE.Restream's main loop now advances its iterator before streaming, since the stream in/out events let scripts remove the current element from the active list.Known behaviour worth calling out:
setElementTypeStreamDistance("object", …)also applies to low-LOD objects, which otherwise default to 1700.Motivation
Fixes #5132.
Every streamed element currently shares one fixed radius per streamer, which is wrong in both directions. Large custom vehicle and object models appear abruptly in front of the camera (wheel/suspension settling artifacts), while small decorative objects are still drawn at 500 units and waste GPU time on lower-end machines. Doing this in Lua means a per-frame distance loop over every element, which is expensive at high element counts and is not frame-accurate. Exposing the streamer's radius at core level fixes both cases with no scripting overhead and no network round trip.
Test plan
Built and tested on Windows / Release / Win32.
Client Deathmatchcompiles and links cleanly, no new warnings in the touched files.Tests_Client(gtest): 304/304 pass.In-game, with a test resource:
setElementStreamDistance(obj, 50)— the object streams out at ~50 units and back in when approaching; confirmed viaonClientElementStreamIn/onClientElementStreamOutlogging.setElementStreamDistance(veh, 600)— the vehicle streams in at ~600 and out at ~650 (hysteresis), with no pop-in and no blocking load, confirming the model was pre-cached.setElementModelStreamDistance(411, 700)applies to all Infernus vehicles; a per-elementsetElementStreamDistanceon one of them wins over the model value, which in turn wins over the type value.setElementTypeStreamDistance("object", 100)takes effect immediately on already-created objects, and on objects created afterwards.nilas the distance resets to the inherited value;getElementStreamDistancereports the effective value at each step.CClientManager).Regression checks:
setElementStreamable,isElementStreamedInand low-LOD object handover still behave as before.Checklist