Big performance issues

Plugin is using hook "OnPlayerTick" that is called ~6 times per second for each player. In that hook you loop through all players with 1 raycast for each.

Raycasts per second formula

Players * Players * 6​

Stats for different online (if in one area)
50 pop = 15.000 raycasts per second
100 pop = 60.000 raycasts per second
200 pop = 240.000 raycasts per second​

 

There are check for DISTANCE, but even with distance check depending on ~200m distance there will be nearby 20 people in that area that will be
2400 raycasts per second

+CanNetworkTo hook that will actually destroy the server

This plugin isn't meant for huge servers.

Merged post

Also, not to mention the minimum distance check, that maximum distance check. Which by default is > 100 metres and < 500 metres. If a player does not fall into that, a raycast is not completed.

Merged post

But, yes. I agree, this is really the only way to complete a plugin like this

"But, yes. I agree, this is really the only way to complete a plugin like this"

There are other ways to achieve that result. Colliders and triggers instead of looping, different networking interaction

I don't understand how you would use a "collider" as raycasting is basically the same. Triggers wouldn't do anything? How would you possibly use a trigger to complete something like this? A circle radius maybe? Still would use more than just checking the distance. Also, there are no "different" networking interactions. There is only CanNetworkTo and if you remove it, the player will be visible in a few seconds.

Merged post

Not to mention that my CanNetworkTo function only takes in players, not entities. So it will only execute for BasePlayer networking.

Merged post

Also just did some testing. 300 loops for 300 players (all raycasted) takes ~115ms. With the new update at 0.5ms and 1k world I'm getting ~2750FPS

Also just did some testing. 300 loops for 300 players (all raycasted) takes ~115ms. With the new update at 0.5ms and 1k world I'm getting ~2750FPS

Don't try to calculate it by FPS, thats kinda stupid idea

9ADDhLsKuRub4Vv.png Orange
Also just did some testing. 300 loops for 300 players (all raycasted) takes ~115ms. With the new update at 0.5ms and 1k world I'm getting ~2750FPS

Don't try to calculate it by FPS, thats kinda stupid

Well, it's the server's performance. And the lowest FPS I saw was 2400



Merged post

Either way 300 players raycasting to 300 players with 115ms. Which is an unlikely situation.
6K3xD9D0vhvYici.png birthdates

Well, it's the server's performance. And the lowest FPS I saw was 2400



Merged post

Either way 300 players raycasting to 300 players with 115ms. Which is an unlikely situation.
On empty 1k server. Now give a try on 100-200 pop server with 3-4k map and you will see INSTANTLY server will be dead with fps drops and freezes

Merged post

+ don't forget other plugins, like 50-80 in common

300 squared is 90000 raycasts per 0.5 seconds aka 180000 raycasts per second. 90000 raycasts and distance checks only took 115ms. And if that's an issue, up the check interval; pretty simple. That means we're doing 300 ticks for each player, and 300 raycasts for each player, even though 300 players being in < 500m & > 100m is unlikely. I'll do another test for you. 6k map, 128k entities, same raycasts (300 squared per 0.5 seconds). Before I had 400-500 fps. After, with again an average of 115ms, I average 200FPS with a low of 170.

Merged post

And in my personal opinion, if it's > 30 FPS, the server is playable.

The issue with CanNetworkTo is that, even if you set the signature to only BasePlayer, Oxide knows you are subscribed, so every time the hook is called from the assembly, Oxide has to loop the methods in your plugin to identify the most appropriate one based on signature. I have benchmarked this and it is by no means free. This time is also reported by Oxide into the plugin's total hook time even when your method is not called, because your plugin is causing Oxide to have to perform that work.

Practically, the cost of this lookup is less than one microsecond on a powerful machine, but make no mistake, that is incredibly expensive, if you consider how often that hook is called. For example, when a player spawns or teleports to an area, a hundred thousand entities may need to be streamed to them from nearby. That could total to double or triple digit milliseconds (resulting in multiple frames dropped basically). Combine that with a high pop pvp server where several players spawn or teleport per second, and a quarter of the CPU budget is taken by just the hook subscription, even without your method being called.

If you have a better method, feel free to let me know.

Here's what I see that can be optimized.

Distance checks
For each player (i.e., recipient), when determing which other players the recipient can see (i.e., senders), instead of looping all players, loop only the players in the network groups the recipient is subscribed to (including the global network group). Doing this should allow you to perform fewer distance checks. Note: This is a fairly minor optimization since the distance checks are the least expensive of the methods being used in this plugin.

Raycasts
  1. If a player was visible for the previous raycast, stop raycasting from the recipient player to that sender player for a few seconds. The consequence of this is that when either player goes behind an object, the recipient would still receive updates from the sender for a few moments, but that's not a big deal and saves lots of raycasts for players who are continuously visible to each other. Note: This is a fairly minor optimization since it does not improve the case where they players are constantly not visible to each other, which is probably the majority case.
  2. Implement a system of risk levels to completely exempt most players. For example, implement a standalone plugin whose sole purpose is to assign players risk levels that other plugins can consume. That plugin can derive player risk levels from a variety of sources, including EAC events, anti-cheat plugin events (e.g., Arkan, stash traps), 3rd party information (e.g., steam account age, hours in game), hours on server, affiliation with other high risk players, and possibly other 3rd party community databases. If a player (recipient) has a sufficiently low risk level, this plugin will not perform any network-limiting logic for them, so they will be networked all players in their network groups.

Component lookup
Don't call GetComponent in CanNetworkTo, since GetComponent is relatively slow. Instead, use dictionaries for faster lookup.

CanNetworkTo

Instead of using CanNetworkTo, implement a custom network group provider. This will be mostly copy-pasta of the vanilla positional network group provider, but you can use alternate logic for players. Basically, each recipient player who has limited network visibility (i.e., the high risk players), will have a separate network group dedicated for sender players that recipient can see. Players should be disallowed from the standard positional network groups and instead only placed in network groups of the recipients who should be able to see them. When you want to toggle visibility for a sender player, toggle their presence in the recipient's network group. This approach can save a lot of overhead, but don't expect it to be simple or straight-forward to implement.

NKXTQs24ExGTuL8.jpg WhiteThunder

what I see

thanks very much for this.. im new.. and didnt understand most of it.. but enjoyed the read. cheers