#plugins-dev-chat
1 messages ยท Page 30 of 1
i made it so you can
THEY HANDED ME THIS and told me it was something else
then i got really drunk fast after just a drink
and that night was the worst one
i started laughing so hard for no reason
i totally don't remember anything other than 2 stuff
bruhhhhh

some people can't grasp the existence of bisexual people
so
don't be surprised if they don't get gender identity
nah he knows
trust me
i fought a freaking war in youtube comments
he knows because his best childhood friend is gay

crazy part he told me "yea don't do it, you know you would be the worst example"
do i care? no
transition
right
good luck :3
HumanRole is base game stuff
then why need Exiled.CustomRoles
idk
i extend the base-game HumanRole type
i didn't know HumanRole was real and could be used only on server
as long as what's written to the network is consistent with base-game, you're fine
why is the Interfaces namespace not in the API namespace
also naming it LabApi.CustomRoles is uhh
kinda makes it seem like it's official
that easier access
you have one ICustomRole interface
and a CustomRole abstract class split up into 10+ parts

designed for possible expansion
putting every 3 members is
why
๐ญ
if you already have those then why // ------------ PUBLIC INSTANCE METHODS
?
i dont love 3000 lines in one file
but instead of a comment, it would probably be better
that's fair, one shouldn't be 3K lines
but navigating this file tree also isn't very dev-friendly
it's your project though, just giving an opinion
maybe i should't be the one speaking
-# ternary operators are really cool :3

Just an organizational nightmare
@random scaffold https://github.com/northwood-studios/LabAPI/pull/139/files we might offer a type enum in the future thats not based of the name(for both door and room)
IDE already allows you to collapse methods
Add a healthy amount of comments and it doesn't matter if you have a huge class
Assuming you can't split it up, I usually split up code into components to place on a gameobject if I can (and it makes sense)
For stuff like a base class, if it makes sense for it to be one class, doesn't matter if it's 500 or 5000 lines long as long as it's well documented
when ever i need to find something is a class i just use this, its all in alphabetical order so its way quicker than scrolling over the entier file
yeah
i wanna collapse regions
I pay for Rider, I'm using all of Rider
I mean hey if you wanna use regions feel free to obviously, but it's just that my mentor and a few other people at the time considered them clutter and I kinda see it
ive never once used this ๐ญ
idk, i use ctrl + ,
structure view can be goated
double shift

https://github.com/Axwabo/SCPSL-Helpers/blob/main/Axwabo.Helpers/PlayerInfo/PlayerInfoBase.cs
this one for example
i can hide the obtainer stuff in my IDE
or the properties
I don't see the point of hiding stuff because I generally know what I'm looking for and the rest won't bother me
less distractions
I also have a very specific order for class members
so I know exactly where I would've placed something
yeah code arrangement is nice
i don't use it in Rider cuz i change my mind sometimes lol
i've got tortured with StyleCop couple years ago and from that time i always write clean code
i wish stylecop enforced more rules tbh
i have to do it in my ide
things like file scoped namespaces, ordering of methods
you can't make it handle file-scoped namespaces???????????
well you can't make it enforce one way or another
anyways
if anyone cares i figured out how to bypass the hint size limit
let me guess
TextHintParameter
multiple
string text = new('m', ushort.MaxValue - 10);
player.SendHint("{0}{1}", new Hints.HintParameter[] { new StringHintParameter(text), new StringHintParameter(text) }, duration: 10);
yea lol
that
ig placement of all class members based on modifiers, naming conventions, hardly ever use var etc.
um
so
var makes sense in contexts where the type is concrete
var is great for linq and complicated type bullshit
clean code is opinionated
ye
I always use var
i only use it for complicated types
it depends for me
but that's just me, and i keep my code as simplest as possible
not writing a 100+ line method
minimizing indentation where possible
i only rarely use var bc its kind of frowned upon in c# but in rust you use type inference as much as possible
"minimizing indentation"?
guard clauses
oh yea
ah, never nesting
instead of if in if in if in if in if in if
in for
in foreach
in try
in using
gosh, people should know about block-scoped usings
i only use guard classes for simple shit like parameter validation
shh it's guard clause
basically
i meant
using var stream = File.OpenRead
instad of using (var stream = File.OpenRead()) {}
try/catch in game dev ouch
the latter has some use cases
but most of the time you need the variable in the scope
anytime I see one I throw it out
a
dont look at how i implemented nuke radiation
you don't use try-catch anywhere?
nope, if you're using it you can probably just fix your logic so it doesn't throw
i have 51,2 hours on scpsl
okay
do u guys add a null check like everywhere
you still have to catch system exceptions though
part of the try/catches I threw out
yeah so
it would randomly throw errors
nres i should clarify
and break the entire coroutine
doesn't anymore, but I did go through like, the entire codebase to add some null/destroyed object checks
unity lifetimes ๐
you can use the destroyCancellationToken now
it was a coroutine that was running 24/7
which admittedly was not the best idea in the whole world but
in your own code, yeah
but there are cases where you need a try-catch
OS related stuff
it still pisses me off that c# doesn't have an elegant way to automatically throw nres if something is null
sad
could add it yourself
well you have the argument null exception
like it exists but its
annoying to write the ifs
I guess
i mean
for one offs like that
but if you have try/catch in gameplay code, you probably screwed up somewhere
true
i like the rust approach where nres don't exist (for the most part)
TrySomething(..., out var result) everywhere
pretty much

null/validity checks
and you're good to go
make a Player.IsValid() extension method and it's all i need
IsValid as in..?
IsOnline?
there's an IsOffline prop but it uses the gameobject which throws...
isnt there a IsDestroyed prop
not on players no
still would be needed since GameObject doesn't check the hub is valid before accessing
you only have Option<T>s
lol
I made a PR for that though
yup
saw
get merged pls
no the fuck they didn't
did they seriously add the dumb offline api from nwapi
i don't think Player.cs was written by one person
so they didn't check
there are a lot of inconsistencies
return player != null && player.ReferenceHub && player.ReferenceHub.gameObject;
IsValid
usages of null conditionals on unity objects
yeah I made that mistake and was going insane as to why I was still getting NREs
fuck Unity
god i hate the if (player.ReferenceHub) thing idk why
WDYM NULL PROPAGATION DOESN'T WORK AAAAAAAAAAAAAAAAAA
:3333333
idk you get used to it ig
I'm used to it because of Unreal since everything's a pointer anyway hah
if (IsValid(SomeObject)) == if (SomeObject) for unity's monobehaviours
oh ik
is the gameObject check necessary? since if the component is valid the gameobject would be too
not sure why you'd check if the gameobject is null
riptide was faster than me
Potentially not necessary but I might as well throw it in there to be certain
oh
right
I see now
yeah I can get rid of it heh
only brining it up because in a future PR i change all the IsDestroy method in LabAPI from Base == null || GameObject == null to just Base == null
smart
๐
fixed
also about those weird properties in Player, ill look into removing/obsoleting them
I'd probably just rename it to Player.IsDestroyed
to be in line with all of the other wrappers
i agree, thats should be coming soon
are any of u good at design i cant figure out how i want to do this shit
what kind of design
:plot:
wdym
i have a class that is intended to be readonly (Element), and its supposed to let you have hintparameters
but you're not supposed to be able to modify or change the hint parameters once you add them (ill make a wrapper for each parameter to ensure that)
problem is how the fuck do i make that happen? i suppose i could just make an Element.AddParameter(Parameter) but that feels a bit awkward
actually the latter idea wouldn't be awful
the uh what
basically:
every player has a Display, this display contains a list of Elements - each element provides a string to be combined into a final hint (so its like an individual hint)
but to prevent weird behavior elements are, for the most part, readonly - if you want to update the content of one you need to create a new element
the problem is thus how do i let you add parameters to an element if its supposed to be readonly?
so you can't retroactively add it
if i make it in take in a list, you could update the list
create an ElementList class
only expose a ReadOnlyList of the elements
and make the constructors create a new list
you can implement IReadOnlyList or just wrap it
or IReadOnlyCollecction
yea thats what i was thinking but
not a big fan of creating a new list every time
if only we had rust ownership
wdym
for the constructor
a constructor with 1 element param, 2 element params
because you could then pass in a list and modify the list
which the ienumerable would reflect
you .ToList it
OH you mean still make the list
yea
also the elements store the parameters
im using init only setters for the most part
that's good too
next up on my optimization tests, gonna modify the server position distributor to do work round robin
odd players one frame, even players the next
why not better just rename
PITA to have to update large codebases
because thats enum is for the name
it doesnt fit well for type, and alot of rooms by default dont have one assigned so imo its abit anoying
just add .None
yeah renaming the enum type is a breaking change so kinda pointless to put a obselete on the prop
?
most of the humans dont need type for each room
only like hcz049, gr18, scp127 and other
did you read what i said earlier
is there an event that fires whenever a player damages another player? I can't find it
PlayerHurt event
Hurt
was looking under PlayerDamage
or Hurting if you want before damage happens
ty
wonder how much of a performance boost this would provide
Feel free to share any results
will do
Taking inspiration from Fortnite, they do the work round robin for player states because of how expensive doing updates for 100 players is
what do you mean "round robin"
oh
ngl i dont know how much you'd gain from that
esp since patching a method makes it slower naturally
at least calling it
can't be slower than like
the fpcserverpositiondistributor atm anyway
Already implemented caching for RelativePosition in FpcServerPositionDistributor.LateUpdate and it did give me some nice gains
david...
eve..
you...
can u make it so overwatch players dont count for players to start the round
(Guys how the fuck do I find out why the player died)
"i just wanna say.. uhhhhhhh..
i.. I HATE you :3"
-# -eve, probably sometime
over on my watch
PlayerDiedEvent or something
PlayerDeathEvent
oh
what the fuck why does it do that
smartwood
no wonder its tanking tps for higher player counts thats 2500 a frame for 50 players
Got a nice boost, not enormous but enough to be visible on graphs
check the DamageHandler
im impressed all my optimizations were micro optimizations
next up: LabEngine
There isn't one for environmental damage
UniversalDamageHandler
How should I have known
contains the translation id
How about..gameplay wise?
also the handler should have a ServerLogsText if you want natural language output
the DeathTranslations class has the death translations
you canc check against those
No change in visible behaviour if that's what your asking. It's not very noticeable to players but the servers raised their average TPS on higher player counts a bit
Yes but how about when firing
Im pretty sure that might cause issues
How so
Are we talking about round robin or the relative position caching
Probably..
the FPC tracer is seperate from the distributer, it creates a new relative position every 4 fixed updates
tbh at a send rate of 60hz I don't think round robin would really break backtracking
I'll look into the FPC tracer and see if I need to patch it too
i would say its unlikely to break anything with out SL has backtracking set up
that means
you could make them use vector3 instead
backtracking is super lenient in SL\
yeah it should be anyway
We'll find out once I'm done writing the transpiler for this
kinda wish this loop was a for loop so I didn't have to insert an index heh
var index = 0;
foreach ()
{
var position = list[index++]
}
you just realized
its kind of funny cause like
its Never used as a hashset and doesn't really have a reason to be one
but i guess whoever designed it wanted fast removals
๐ญ
NW please tell me you're gonna fix this
dunno if I can even transpile it to something else
probs not if there's no other list of hubs
well uh
create an int local
add 1 every iteration
if someone gets removed between calls
well crap
u cant really
forward iteration won't matter here
i meant replacing AllHubs but like, yeah nah
also for index I can probably just get away with player id
u can make ur own List<ReferenceHub> ig
probably don't
praying NW fixes this ouch
i doubt they would
but yeah for index I'll just create an index local and increase it every iteration
not really an internal breaking change but for plugins using it, oof
I mean
or a bool
LabAPI plugins should use Player.List/ReadyList
benchmark
shouldn't matter aren't C# bools 64 bit anyway
old code though
what
it's 1 byte
yeah I know
hahaha
nvm
i meant more like
the operations needed to set a bool and increment an int
but yeah I misworded it
dw
32
all types less than an int are widened
right
checked and the IL for flipping a bool and the IL for incrementing an int are both 4 operations
but by using a bool I don't have to modulo it
so better
need to figure out how to add a condition to an if statement
labels
what do you mean
there's no if in IL
oh you mean which jump
need to add the odd/even check there
instruction?
aye
wheres this code
me when id just do the if call inside of my method
FpcServerPositionDistributor.LateUpdate
not performant enough
yeah I'm transpiling this because it's already a hot path and I don't wanna make it any hotter
brfalse to the "skip" label
but wait
or use a codematcher (the best way of doing transpilers0
yeah im using a codematcher atm
I mean it's the most update-compatible way
if I start using offsets, NW updates that method and I get headaches
so how do I get a label from a codematcher
guessing I MatchEndForward these 3 instructions and then...?
iirc its like
"Find those 4 instructions, then insert my instructions afterwards"
why nop
ohhh that makes so much sense and actually so useful
๐
how do we return this back tho?
return matcher.Instructions();
.InstructionEnumeration()
or that ig
ah
i think they're objectively better than normal transpilers
"i think they're objectively better"
they're probably objectively better
don't think :3
but if it's objective then it's not really an opinion, more of a fact
pai is objective pai
me when
specify the created type
because you can't do everything with labapi..?
im not used to transpiler, im not sure what the type should be
example?
says it there
CodeMatch
ohh
if you have a single op to match it fails yeah
ok

me when it's not
use the IDE, people
1 sec
MatchEndBackward puts the index at the last instruction
that matches that
don't use backwards
ok
so what do you recommend for index - 1 then?
tradition
i usually only do prefix or postfix
Im taking you
that's it
Ur my dev now thanks
LET HER FOCUS ON RUEI V3
them but
negative
focus on evehints
U cant
me when cables :3
sending hr report
sanity check, if I want to pass in a local to a function call, I load the local and then call the function right
jk too lazy to do that again
hr cant do anything about me eating ur cable!! teehee
What
yw
yea
someclass.function(myLocalBool)
Im too braindead to understand what ur trying to say
is the method static
Oh
if not u need to load the class to call it on first
huh
Ldloc(index)
ye
don't write transpilers unless absolutely necessary
maybe tomorrow
I find them cool but also don't like them in terms of maintenance cost
It makes me learn them
if NW updates something a transpiler modifies it could just break
However
Why would nw update code for round start
what are you trying to do
me when local indexes change
and the numbers in compiler-generated classes' names change
Learn transpilers :3
no i mean in round start
Also modify ScpSpawner and also
Modify the round start player count
Make ow not count
:)
Its just to a reason to learn transpilers
it already shouldn't
interesting
Has been doing that since ages
example
It was never changed to not count OW afaik
then yeah have fun modifiying RoundSummary._ProcessServerSideCode
Uh actually
i tried do that
Is that it?
yeah it is
it didnt work well
I dont think thats it
that's the coroutine that checks if a round should end
Im referring to before
Thats not it
yes
okay yeah I see
When round is starting if 2 overwatch players are only players on round will start and then end immediately
Hence why i want to change that
sec
my patch on it?
might be? what was the patch again
mayhem victory thing
oh
yea i love codematchers idk
i wish people used them more
newer harmony versions make them even better
@hearty shard CharacterClassManager.Init
I know
Thats not the issue
The issue is
Im in bed
And ive never written a transpiler before
So this will be fun
yep, awesome
ldarg.0
ldarg.1
Z call Method(X, Y)
// z is now on stack
pai so smart
ah wait
got the wrong label
for skipping the current iteration
label i'm currently matching is ending the loop

#1 nerd
see and with a normal transpiler
Pai
that would've taken 3 years to figure out
Can you rewrite harmonyyyy :3
i WISH
why rewrite perfection
Never speak again
Harmony is hell on earth
It does???
yea
Damn
on your desktop
oh i didnt realize that LOL
i always made a LogTranspiler method
### Patch: static PlayerRoles.FirstPersonControl.NetworkMessages.FpcSyncData PlayerRoles.FirstPersonControl.NetworkMessages.FpcServerPositionDistributor::GetNewSyncData(ReferenceHub receiver, ReferenceHub target, PlayerRoles.FirstPersonControl.FirstPersonMovementModule fpmm, System.Boolean isInvisible)
### Replacement: static PlayerRoles.FirstPersonControl.NetworkMessages.FpcSyncData PlayerRoles.FirstPersonControl.NetworkMessages.FpcServerPositionDistributor::PlayerRoles.FirstPersonControl.NetworkMessages.FpcServerPositionDistributor.GetNewSyncData_Patch0(ReferenceHub receiver, ReferenceHub target, PlayerRoles.FirstPersonControl.FirstPersonMovementModule fpmm, System.Boolean isInvisible)
IL_0000: Local var 0: PlayerRoles.FirstPersonControl.NetworkMessages.FpcSyncData
IL_0000: Local var 1: PlayerRoles.FirstPersonControl.NetworkMessages.FpcSyncData
IL_0000: Local var 2: PlayerRoles.FirstPersonControl.NetworkMessages.FpcSyncData
IL_0000: // start original
IL_0000: ldarg.0
IL_0001: ldarg.1
IL_0002: call static PlayerRoles.FirstPersonControl.NetworkMessages.FpcSyncData PlayerRoles.FirstPersonControl.NetworkMessages.FpcServerPositionDistributor::GetPrevSyncData(ReferenceHub receiver, ReferenceHub target)
IL_0007: stloc.0
IL_0008: ldarg.3
IL_0009: brtrue => Label0
IL_000E: ldloc.0
IL_000F: ldarg.2
IL_0010: callvirt PlayerRoles.FirstPersonControl.PlayerMovementState PlayerRoles.FirstPersonControl.FirstPersonMovementModule::get_SyncMovementState()
IL_0015: ldarg.2
IL_0016: callvirt System.Boolean PlayerRoles.FirstPersonControl.FirstPersonMovementModule::get_IsGrounded()
IL_001B: ldarg.1
IL_001C: callvirt UnityEngine.Transform UnityEngine.Component::get_transform()
IL_0021: callvirt UnityEngine.Vector3 UnityEngine.Transform::get_position()
IL_0026: call static RelativePositioning.RelativePosition GameOptimizer.Patches.RelativePositioningCache::GetOrCreate(UnityEngine.Vector3 position)
IL_002B: ldarg.2
IL_002C: callvirt PlayerRoles.FirstPersonControl.FpcMouseLook PlayerRoles.FirstPersonControl.FirstPersonMovementModule::get_MouseLook()
IL_0031: newobj System.Void PlayerRoles.FirstPersonControl.NetworkMessages.FpcSyncData::.ctor(PlayerRoles.FirstPersonControl.NetworkMessages.FpcSyncData prev, PlayerRoles.FirstPersonControl.PlayerMovementState state, System.Boolean bit, RelativePositioning.RelativePosition pos, PlayerRoles.FirstPersonControl.FpcMouseLook mLook)
IL_0036: br => Label1
IL_003B: Label0
IL_003B: ldloca.s 2 (PlayerRoles.FirstPersonControl.NetworkMessages.FpcSyncData)
IL_003D: initobj PlayerRoles.FirstPersonControl.NetworkMessages.FpcSyncData
IL_0043: ldloc.2
IL_0044: Label1
IL_0044: stloc.1
IL_0045: ldsfld System.Collections.Generic.Dictionary`2<System.UInt32, System.Collections.Generic.Dictionary`2<System.UInt32, PlayerRoles.FirstPersonControl.NetworkMessages.FpcSyncData>> PlayerRoles.FirstPersonControl.NetworkMessages.FpcServerPositionDistributor::PreviouslySent
IL_004A: ldarg.0
IL_004B: callvirt System.UInt32 Mirror.NetworkBehaviour::get_netId()
IL_0050: callvirt virtual System.Collections.Generic.Dictionary`2<System.UInt32, PlayerRoles.FirstPersonControl.NetworkMessages.FpcSyncData> System.Collections.Generic.Dictionary`2<System.UInt32, System.Collections.Generic.Dictionary`2<System.UInt32, PlayerRoles.FirstPersonControl.NetworkMessages.FpcSyncData>>::get_Item(System.UInt32 key)
IL_0055: ldarg.1
IL_0056: callvirt System.UInt32 Mirror.NetworkBehaviour::get_netId()
IL_005B: ldloc.1
IL_005C: callvirt virtual System.Void System.Collections.Generic.Dictionary`2<System.UInt32, PlayerRoles.FirstPersonControl.NetworkMessages.FpcSyncData>::set_Item(System.UInt32 key, PlayerRoles.FirstPersonControl.NetworkMessages.FpcSyncData value)
IL_0061: ldloc.1
IL_0062: // end original
IL_0062: ret
DONE
u can prob do that without codematchers
F
it looks so much nicer...
wait nvm the label is correct
goes to the end of the loop but jumps back to the start if we've still got elements
im stoopid
also have u ever seen the internals of harmony
no and i won't ever look at them
yea
even then
NOTHING IS DOCUMENTED!!
and the things that are have the most useless documentation
also can I tell the codematcher to set its position to a specific label
eh it's fine I'll just inject the instructions when I find the label
instructions.FindIndex(x => x.Labels.Contains(label))
guess that'd work yeah
aight time to test it
matcher.InsertAndAdvance(
new(OpCodes.Ldloc, isOdd.LocalIndex),
new(OpCodes.Ldc_I4_0),
new(OpCodes.Ceq),
new(OpCodes.Stloc, isOdd.LocalIndex)
);
oooh ty
ah fuck
the IL looks fucky
can i see ur codematcher
dm'd
guess i need to move to the next instruction
matcher.Advance(1).Insert...
huh
Cyn and it's transpilers
im stupid it couldn't match anymore because I added my instructions
lol
IL looks perfect now
i'd test it out but dummies won't cut it since they don't walk much bleh
uh oh
100
how am I making it worse with this
nvm
it is better
without
okay it's gotta be some other code
it's only because they were captains
What did bro do
Try with 254 player
with
seems to be a bug
one of the QA testers doesn't see other players move
but another sees us fine
i do see some wearablesync exceptions
probs the dummies
[2025-05-25 14:44:19.063 -07:00] [STDOUT] InvalidOperationException: Unable to create WearableSyncMessage with payload of 183.
[2025-05-25 14:44:19.063 -07:00] [STDOUT] at PlayerRoles.FirstPersonControl.Thirdperson.Subcontrollers.Wearables.WearableSyncMessage..ctor (ReferenceHub hub, PlayerRoles.FirstPersonControl.Thirdperson.Subcontrollers.Wearables.WearableElements flags, Mirror.NetworkWriter payloadWriter) [0x00046] in <2343be033e9f4e37923f780ece756d8e>:0
[2025-05-25 14:44:19.063 -07:00] [STDOUT] at PlayerRoles.FirstPersonControl.Thirdperson.Subcontrollers.Wearables.WearableSync.OnHubAdded (ReferenceHub hub) [0x00066] in <2343be033e9f4e37923f780ece756d8e>:0
[2025-05-25 14:44:19.063 -07:00] [STDOUT] at (wrapper delegate-invoke) System.Action`1[ReferenceHub].invoke_void_T(ReferenceHub)
[2025-05-25 14:44:19.063 -07:00] [STDOUT] at ReferenceHub.Start () [0x00009] in <2343be033e9f4e37923f780ece756d8e>:0
no exceptions but still not moving hmmm
ah odd players can't see anyone move lol
probs fucked up my logic
Interesting
Gonna fix my logic but @unique crane since you wanted results of round robin ^
those results do include relative position caching which gives like 4-7 ticks by itself
but the rest is just round robin
yeah we will once I get that odd players logic fixed
Hubert...
David when fix
fix deez
BE QUIET
now to figure out why the fuck my even/odd logic is screwy
IL_007E: ldloc 2
IL_0082: not
IL_0083: stloc 2
kinda wonder if it's not
@grand flower If you want, next up you can look at animations (IK)
Maybe you can optimize that 
(thats like top 1 performance heavy currently)
Oh
Can you show everything :3
Im gonna do horrible things just u wait
):
serverside? D:
https://cdn.discordapp.com/attachments/1348879302036557885/1376321532036644934/SCP-_Secret_Laboratory_-_2025-05-25_15-10-10.mp4?ex=6834e6d0&is=68339550&hm=b589f292cfad02156f5ac9aed38b9dc03f3023cfe87d1606b83eb1bf9593ed69& at high speeds round robin does provide some jitter but like, 255% speed. 100% movement boost has no issues/
https://cdn.discordapp.com/attachments/1348879302036557885/1376321826715861012/SCP-_Secret_Laboratory_-_2025-05-25_15-11-21.mp4?ex=6834e716&is=68339596&hm=d86ee53abed1a495992492dc9eba9049a7525b95c604b724579f95b58fde42df& also has a lot of jitter for 173 - but like, it's 173, it doesn't happen when you're looking at it.
for the performance boost it's worth it IMO

Will test with actual players soonโข๏ธ
interesting
also backtracking was still working, we'll have to see with actual players (as always) but it seems like there's very little cons to this
turn on cheats and test

Or that.
i'll have to check how expensive it is and if i need to patch it
I forgot about shooting
but i doubt it
hell with round robin I don't think I'm gonna be doing much more optimization after this
Using a raycast to look at a Pickup on the ground, how would go about to get the Pickup as a reference?
GetComponent<Pickup> on the collider?
@hearty shard Pickup.Get only accepts a ushort or an ItemPickupBase
-# not sure what an ItemPickupBase is
ItemPickupBase is SLs Pickup
Pickup is the labapi wrapper of it
You might be able to Pickup.Get(GetComponent<ItemPickupBase>)
Will try that, thank you+
if that doesnโt work, usually what works for me on colliders is collider.transform.root.GetComponent<ItemPickupBase>()
every time I try to do GetComponent on the collider i get null so, yeah
ev.IsAllowed = true in Hurting event is not working for two members of the same team, I'm def missing something but idk what it is
Is this with FF on?
^^
Hurting is when damage is attempted
IsAllowed is always true in that case i believe
You'll need a work around
patch in your own event
@unique crane Tested with a few players, no discernible gameplay changes other than the jitter in the videos
The performance boost is also tied to the resources the machine has
If the server is given a single core, the performance boost (while there) isn't as good
But otherwise still a nice thing for servers that need a little boost
Especially high player counts, I think you'd be able to play at a stable 30 tps w/ 100 players.
The issue would likely be client performance at that point
This is still useful for 48/64 player servers as long as you don't starve it of resources
Try setting ForceFullFriendlyFire (FFFF) in the damage handler to true
What did you do?
Wizard level trickery
Implemented round robin to the FpcServerPositionDistributor, helps a lot with server performance on higher player counts
^
How did you handle even/odd frames jf you got around to it?
I have an idea but I wanna know if you have a solution
Added a bool to the method and just flipped it before it looped
And within the loop it keeps track if the current hub is odd or even with another bool
How do you track it between calls tho
Wdym between calls
The bool that tracks whether the current frame should treat odd/even is a static if that's what you mean
A field?
I added a condition to the loop that checks if the update should skip the player
Yeah
Sec
Ye
So like
ldfld State
ldc.i4.0
ceq
dup
stfld State
stloc localState
Not sure if the bitwise not instruction is faster than ceq, have you benchmarked it?
bitwise not didn't work
for some reason
I could probably not make it a function call but like
negligible
multiple ways of doing it but one is you update odd players one frame, and then even the next
so you cut the amount of updates per tick in half
and then inside the loop @upper vapor
_isEven is the frame state, isEven is whether or not the current hub is even
then I just check if we should skip the hub by adding a condition
which is just this atm
although in the future I will be skipping dummies/the host player probably and other stuff
also got some plans for adaptive update rates based on the current amount of players alive
but what this is like?
what do you mean
not surprised 
A way to optimize servers with high player counts
yeah I did like
Ldloc, not, Stloc, just didn't do anything, even players would never get updates and saw everybody as standing still 
oh but for what aspect?
Oh
by only sending updates to half of the players every tick
meaning your 100 player server would act as a 50 player server in that regard
interesting
if you can keep a high tickrate you get minimal side effects from doing that
where did you even patch for this?
FpcServerPositionDistributor.LateUpdate
interesting
I've got some other patches in that function that give it some boosts
never thought of that
fire
You can thank Fortnite for it 
wait fr?
It's one of the many tricks they do to run 100 player servers smoothly enough
does it only cause issues for high speeds?
Yeah Epic Games are pretty damn good at that stuff
Cool asf
yes and somewhat minor, i've got videos
What if you dup before stloc and brfalse after stloc
^
In the loop
wouldn't that just be more instructions though
compared to just the current four
Wouldn't you need to load the local again?
that doesnt even look terrible
that's 255% movement speed, so like, hardly ever happens for most servers
and 173's jitter isn't even that bad
ah i see
wouldn't this cause issues with like hitreg
maybe if I get rid of the function call
etc...
nope, was brought up by davidsebesta but it works perfectly fine
backtracking is lenient in SL and still holds up
but im interested to know what else you did
Yeah that would eliminate some nanoseconds
to optimize it
tbf the hot path in that method is the looping, calculating relative positions and sending the data
and I wanna keep the function call & pass in the hub actually
so I can do more checks if I want to be aggressive and do stuff like skipping dummies, the host player, and other custom checks
relative position caching
what you mean?
like you do half and half
for it
thats it?
clientside defo needs some optimization at higher counts
you mean it steals code?
but we can't do it so w/e
so each player is going to create a new relative position?
yes because I will make optifine for SL 
and you just strip them of it?
I cache relative positions in that method so it doesn't compute them more than once per player
so at 48 players, it's 48 computed relative positions, not 48^2
im really into this like how do you manage backtrack and how it works in general
because desync if done improprely would be crazy
backtrack isn't affected by either of those fixes
so if im getting right
waypoints get duplicated?
for each player?
those calculations do yeah
even though they won't change in that loop
for each player combo
so playercount^2
48 players means 2304 relative position calculations
give or take
So how do you manage them?
with caching it's just playercount, so 48
I just have a cache that I clear at the start of LateUpdate
on the FPC distrubution?
and I patched the code to call a function that gets the cached relative position for the given Vector3 if any, or creates and caches it
yeah
The cache is allocated once, I think I set it to like, 128 elements, so in theory it never gets re-allocated
im confused
so you send each data
i just don't see issue with this code
im being serious
check the FpcPositionMessage constructor
yep
private static void LateUpdate()
{
if (!NetworkServer.active || !StaticUnityMethods.IsPlaying)
{
return;
}
FpcServerPositionDistributor._sendCooldown += Time.deltaTime;
if (FpcServerPositionDistributor._sendCooldown < FpcServerPositionDistributor.SendRate)
{
return;
}
FpcServerPositionDistributor._sendCooldown -= FpcServerPositionDistributor.SendRate;
foreach (ReferenceHub referenceHub in ReferenceHub.AllHubs)
{
if (referenceHub.Mode != ClientInstanceMode.Unverified && !referenceHub.isLocalPlayer)
{
if(!Dictionary.TryGetValue(player, out FpcPositionMessage message)
Dictionary.Add(referencehub, new FpcPositionMessage(referenceHub))
referenceHub.connectionToClient.Send<FpcPositionMessage>(message);
}
}
}
so something like this?
nope
the patch for caching the relative position isn't done in that method
the only thing done in that method is clearing the cache at the very start
so you do it on the ctor?
48^2 because each player target each others
yep
You send every player the position of every player
So you just create a dictionary in there add them
You could if you wanted to, I just made it easier with a method
I just dont get how you save the data


