Facepunch / sbox-issues

178 stars 12 forks source link

Changing a static variable requires a s&box restart for the value change to be reflected #4164

Open CarsonKompon opened 11 months ago

CarsonKompon commented 11 months ago

Describe the bug

When using a static variable in a class (or static class), the value cannot be changed without a s&box restart.

To Reproduce

Have any class with any static variable

public class MyEpicClass : Component
{
    public static float GRAVITY = 2f;

    protected override void OnUpdate()
    {
        Log.Info(GRAVITY);
    }
}

Then attach the component to any object and enter play mode so we can see the debug logs. The logs should all say 2. Then change the variable to any other value:

public static float GRAVITY = 9.8f;

Once the hotload is complete, you'll see that the console is still logging 2 instead of the new value. If you hit the play button to exit play mode and press it again to re-enter, then it will still print 2.

If you completely close the s&box editor and re-launch it, then the new value will be printed upon loading your project.

Expected behavior

The value should be hotloaded.

Media/Files

No response

Additional context

No response

handsomematt commented 11 months ago

When would you expect the static variable to change on a hotload?

If you have a static variable that's linked to gameplay that is modified at runtime, you don't want it resetting to it's original value everytime you hotload.

If you want a read only variable defined in code you could do:

public static float GRAVITY => 2f;
anthonysharpy commented 11 months ago

I think the issue is that this also happens when you stop and start the game, not just hotloading

CarsonKompon commented 11 months ago

I think the issue is that this also happens when you stop and start the game, not just hotloading

Exactly this. I'm not super particular about how the hotload functionality should work, but I shouldn't have to restart sbox, I should just be able to press the stop button and start button

garrynewman commented 11 months ago

That's feasible, I would think we could easily run though and reset static variables to their code defined default, somehow.

matekdev commented 8 months ago

This is still happening, and extremely annoying. I have static variables that continue to hold their value between play mode and editor mode.

dictateurfou commented 4 months ago

very annoying

dictateurfou commented 4 months ago

I have found a way to reset static variables. We do not have a GameStart or other functionality that allows us to execute actions at launch before loading the entire scene. The OnLoad component is not a solution because it doesn't necessarily execute in the order we want. In my case, I want to reset the variable before the rest is created. The solution is the GameObjectSystem.


public class MyGameSystem : GameObjectSystem
{
    public MyGameSystem( Scene scene ) : base( scene )
    {
        EventSystem.EventCommand.ClearWhenStart();
    }

}

If someone from Facepunch comes across this, in Unreal we have something similar called SubSystem. I think it would be good to have a similar system. https://dev.epicgames.com/documentation/en-us/unreal-engine/programming-subsystems-in-unreal-engine?application_version=5.4

Toxic-Cookie commented 2 months ago

I'm now dealing with this issue as well. I'd expect static to reset itself just as all other fields and properties reset themselves. It doesn't seem like correct behavior for the simulation to be disposed of and for statics specifically to persist their state.

matekdev commented 2 months ago

I believe this issue can be closed @CarsonKompon

MDFL64 commented 2 months ago

Is the rationale for this not being a bug described anywhere? Is it for editor tooling?

I get that hot reloads are tricky, but regardless of whether statics are good design, having state persist between executions should be considered a bug.

anthonysharpy commented 2 months ago

var something = MyUsefulGlobalObject.MyThing

is much better code than

private MyUsefulGlobalObject myUsefulGlobalObject;

protected override void OnStart()
{
    myUsefulGlobalObject = Game.ActiveScene.GetAllComponents<MyUsefulGlobalObject>().FirstOrDefault();
}

...

if (myUsefulGlobalObject == null)
{
    Log.Warning("myUsefulGlobalObject not found!");
    return;
}

var something = myUsefulGlobalObject.MyThing;

This used to work fine before the move to scene, so it would be a shame if it didn't get added back in

And exactly what @MDFL64 says, whether or not statics are bad (I don't think they are when used sensibly), the bug here is more to do with the fact that state is being persisted across sessions