#LethalNetworkAPI

1 messages · Page 2 of 1

frosty atlas
#

was reading in another thread that with V73, anything doing networking will need fixing... havent had a chance to check, but i guess this needs an update too? x.x

dull field
#

haven't seen that so will take a look after work today

#

ah I see

#

yeah I likely am still good since it uses custom messages instead of rpcs, but I will likely need to adjust a little here and there so I'm still gonna take a looksies

frosty atlas
barren furnace
barren furnace
#

Thanks!

weary flint
#

I feel like I'm probably being silly but I can't seem to figure this out. All I want to do, is have a synced bool. When someone joins the lobby it is false for everyone while some code of mine runs, then it becomes true for everyone. When the next person joins, it goes back to false for everyone and when they're finished, it becomes true again.
Here's where I have that bool.

namespace MaterialAssetRestorerCore
{
    internal static class MaterialsNetworkSync
    {
        public static LNetworkVariable<bool> materialsInitialized = LNetworkVariable<bool>.Connect(
            identifier: "materialsInitialized",
            onValueChanged: OnMaterialsInitializedChanged,
            offlineValue: false,
            writePerms: LNetworkVariableWritePerms.Everyone
        );

        private static void OnMaterialsInitializedChanged(bool oldValue, bool newValue)
        {
            MaterialAssetRestorerCore.Logger.LogWarning($"Materials initialized changed from {oldValue} to {newValue}");
        }
    }
}
#

And here's in theory where it changes

[HarmonyPatch(typeof(StartOfRound))]
public class MaterialInit
{
    public static List<MaterialInformationContainer> materialInformationContainers = new List<MaterialInformationContainer>();

    //after StartOfRound, initialize the materials
    [HarmonyPostfix]
    [HarmonyPatch(typeof(StartOfRound), nameof(StartOfRound.Start))]
    public static void InitializeMaterialsPatch()
    {
        CoroutineHelper.Instance.StartCoroutine(MaterialInit.InitializeMaterialsCoroutine());
    }
    public static IEnumerator InitializeMaterialsCoroutine()
    {
        MaterialsNetworkSync.materialsInitialized.Value = false;
        MaterialAssetRestorerCore.Logger.LogInfo("Initializing materials...");
        foreach (MaterialInformationContainer container in materialInformationContainers)
        {
            yield return MaterialGet.GET_material(container.BaseMaterial, container.PrefabName, container.SceneName, (foundMaterial) =>
            {
                if (foundMaterial != null)
                {
                    container.replacementMaterial = foundMaterial;
                }
            });
        }
        yield return new WaitForSeconds(10); //debug testing. REMOVE THIS
        MaterialAssetRestorerCore.Logger.LogInfo("Finished initializing materials.");
        MaterialsNetworkSync.materialsInitialized.Value = true;
    }
}
#

All I want to do is use that bool to determine if the ship lever can be pulled. It seems to work fine for the host, but then they can pull the lever when they're done even if another player joins and hasn't finished the initialization process

dull field
#

there's a couple things that could be the cause here, I'll do some testing/investigating on my end and see whats happening

weary flint
#

Alrighty! Thanks!

dull field
# weary flint Alrighty! Thanks!

okay so I think I figured it out, seems like a logic issue on my part, and then a "race" condition (that always happens); I'll make a write-up and work on publishing the fixes once I get some food

weary flint
#

Oh interesting. Thanks!

dull field
#

okay, so I think I have a fix on my end - doing some validating to make sure

#

but essentially, the network variables weren't getting correctly updated when a client joined the server, which in your case would have the variable still be false, and wouldn't actually update the variable since it believed it was still the same value

#

got a fix so that it correctly updates the variable, as well as gives an error when it hasn't synchronized yet, as well as a new property to see if it's synchronized

#

the race condition on your end is that StartOfRound.Start is before the variables are synced from the server, and thus it isn't actually connected yet

#

I would either a.) wait for the variable to show as synchronizing (I'll add an event to make this easy as well)

#

or b.) use a combination of LNetworkVariables and LNetworkEvents - have the variable controlled by the server which dictates whether the game can start or not, and use the event for clients to let the server know that they have initialized the materials

#

which would fix an issue I noticed where someone could join right before someone else finished initializing materials, and the bool would be true even though someone is still loading (client 1 joins -> false -> client 2 joines -> no change -> client 1 finishes -> true -> client 2 finishes -> no change)

#

aight, I pushed my changes to a branch on the repo, will review tomorrow to make sure I'm not missing anything and release

weary flint
#

Interesting. I’ll check it out sometime. Thanks!

frosty atlas
#

maybe this is why i was sometimes seeing some oddities before... like the client was always 2 steps behind the host... hmm 😮

weary flint
dull field
#

sorry got caught up in other stuff - took another look and made some bug fixes - pushing through now

#

I'll see if I can get wiki update tonight, but otherwise the two main additional things is IsInitialized - whether the variable is connected to the server or not; and OnInitialized - event that gets called when the variable gets initialized (value gets updated on client join, then event is called)

#

lmk if to run into any bugs with the new version

#

should be v3.4.1 (v3.4.0 will work too but not deployed to nuget - issues with workflow fixed in the .1)

weary flint
#

Ok! I’ll try to look into it tomorrow . Thanks!

weary flint
#

does OnInitialized not run for the hosting player? In a StartOfRound.Start postfix I thought MaterialsNetworkSync.waitingPlayerCount.OnInitialized += InitializeMaterialsPatch; would invoke that function but it never seems to until another player joins and then it does run for them (but not the host). I'm probably doing something wrong though unless LAN for some reason is breaking stuff

dull field
#

it should, I'll double check

dull field
#

sorry to get back so late, it seems fine working on my end (LAN); where are you running that code?

#

you should see any logs appear just before [Debug :LethalNetworkAPI] Created UnnamedMessageHandler instance. on the server

#

it'll happen later on the client

#

there is one bit that I overlooked though, and would like your opinion - should the client run the OnValueChanged event, even if the variable isn't synchronized yet (it'll run when the client's value gets updated to match the server value when joining), or should it only run when the variable is synchronized (ignoring the first update to get it to match the server)?

weary flint
weary flint
dull field
weary flint
#

ok, I feel like I need some help. I'm probably doing something wrong but I can't exactly figure out what. So, a piece of current code. In my mind, this should postfix StartOfRound and subscribe to waitingPlayerCount's OnInitialized event (I switched from a bool to a num to avoid person A finishing before B and setting it true) and once it's inititialized the coroutine basically gets kicked off. I'm testing just solo and nothing ever prints beyond "Waiting for network variables...". It just doesn't seem the event ever invokes.

[HarmonyPostfix]
[HarmonyPatch(typeof(StartOfRound), nameof(StartOfRound.Start))]
public static void WaitForNetworkVariables()
{
    MaterialAssetRestorerCore.Logger.LogDebug("Waiting for network variables to initialize before starting material caching...");
    MaterialsNetworkSync.waitingPlayerCount.OnInitialized += InitializeMaterialsPatch;
}
public static void InitializeMaterialsPatch()
{
    CoroutineHelper.Instance.StartCoroutine(MaterialInit.InitializeMaterialsCoroutine()); 
}
public static IEnumerator InitializeMaterialsCoroutine()
{
    MaterialsNetworkSync.waitingPlayerCount.Value++;
    SceneLoadPatches.MuteSceneStateChangeEvents(); //prevent other mods from running code on scene load/unload
    MaterialAssetRestorerCore.Logger.LogInfo("Initializing materials...");
weary flint
dull field
#

I do believe that's the case

#

in my test, I subscribed in my plugin's awake method - you can subscribe at anytime as long as the game's running; just if you subscribe after the variable is initialized, it won't run (until you rehost/rejoin)

weary flint
#

Got it. Alright I’ll try that

dull field
#

I could make it easier by supplying a param in the constructor, although I have to be careful as I don't want to break compatibility with mods not referencing the new version when compiled

#

and the way to get around it looks a bit bad imo (not that it's really a reason to not implement it)

weary flint
#

Subscribing in the awake makes sense tbh. I just didn’t think to do that 🙃

weary flint
#

wait wait wait, actually I can't do that. I need to run in StartOfRound because the prefabs I'm trying to grab materials out of don't seem to exist until then. When I tried subscribing in the awake it seemed it invoked way too early still in the menu. What I'm kind of confused about though is how that invoked seemingly before I was in a game but going somewhat back to the old way, IsInitialized is false in StartOfRound. I should not be doing this so late

dull field
#

hmm... it should be true (on the server) - that might be a bug

#

because I would suggest subscribing & running a check for if IsInitialized is true to run the method anyways, which seems to be the case