Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Server and Client sharing LuaState in single process PIE #37

Open
Ollinator opened this issue Jun 1, 2022 · 4 comments
Open

Server and Client sharing LuaState in single process PIE #37

Ollinator opened this issue Jun 1, 2022 · 4 comments

Comments

@Ollinator
Copy link

Hey there!

I've recently played around with LuaMachine and replication and noticed some weirdness going on: When launching 2 instances in PIE using "run in one process" and executing an IsServer() call would report FALSE on the server when executed through an RPC which calls a lua function calling the IsServer() function. The issue (naturally) does not happen when running with multiple processes, so it appears like the two instances somehow share the same luastate.
I've first experienced it in 4.26, but also reproduced it in 4.27 and 5.0, each with latest plugin version available. Also attaching two test projects demonstrating it both in 4.27 and 5.0. Just run them with 2 players and press space on the client, this will execute the chain of functions and print each step on screen and in the log.
LuaTest.zip
LuaTest_UE5.zip

As its a bit complicated to understand perhaps, lets go through the setup step by step! :D

  1. the LuaState defines a function IsServer() which just returns the result of the engines IsServer() to check the state:
    image

  2. A simple Lua script making a table with a single function that just executes the IsServer() function above and compares it with the expected result (which is coming from a native call of the engines IsServer() function):

TestActor = {}

function TestActor:TestFunction(msg, should_be_server)
    mytype = IsServer() and "SERVER" or "CLIENT"
    wrongType = IsServer() ~= should_be_server

    if wrongType then msg = msg .." ---- wrong type reported!" end

    Print("Lua: "..mytype..": "..msg, wrongType)
end
  1. then, in a test actor, making a copy of the table above and keeping it as a reference for later calls:
    image

  2. in the test actor, a call into the lua function, providing the native result of IsServer():
    image

  3. and finally the chain of RPCs executed in the test actor calling the lua function in each step to report the results of the IsServer() function:
    image

when executing this, this is the print out:

[BP_TestPawn_C_1] Server: BP_TestPawn_C_1: BeginPlay SERVER
[BP_LuaState_Test_C_0] Server: BP_LuaState_Test_C_0: Lua: SERVER: BeginPlay
[BP_TestPawn_C_0] Client 1: BP_TestPawn_C_0: BeginPlay CLIENT
[BP_LuaState_Test_C_0] Client 1: BP_LuaState_Test_C_0: Lua: CLIENT: BeginPlay
[BP_TestPawn_C_1] Client 1: BP_TestPawn_C_1: BeginPlay CLIENT
[BP_LuaState_Test_C_0] Client 1: BP_LuaState_Test_C_0: Lua: CLIENT: BeginPlay
[BP_TestPawn_C_0] Client 1: BP_TestPawn_C_0: Input CLIENT
[BP_LuaState_Test_C_0] Client 1: BP_LuaState_Test_C_0: Lua: CLIENT: input
[BP_TestPawn_C_1] Server: BP_TestPawn_C_1: received ServerRPC SERVER
[BP_LuaState_Test_C_0] Client 0: BP_LuaState_Test_C_0: Lua: CLIENT: on server ---- wrong type reported!
[BP_TestPawn_C_1] Server: BP_TestPawn_C_1: received MulticastRPC SERVER
[BP_LuaState_Test_C_0] Client 0: BP_LuaState_Test_C_0: Lua: CLIENT: multicast ---- wrong type reported!
[BP_TestPawn_C_0] Client 1: BP_TestPawn_C_0: received MulticastRPC CLIENT
[BP_LuaState_Test_C_0] Client 1: BP_LuaState_Test_C_0: Lua: CLIENT: multicast

As you can see, both client and server execute within the same lua state, and despite the native call reports to be the server properly, the call from the lua function (executed from the RPC) is reporting on the server to be "Client 0".

I'm quite sure this has to do with the lua state being a singleton, but for some reason client and server share the same singleton instead of creating its own. Not sure if that is easily fixable or at all. Workaround for now is to always run in multiple processes, but this makes testing and debugging really slow and cumbersome...

Maybe I'm also doing something stupid though :D

Thx for your support and that awesome plugin!

@bfoo75
Copy link

bfoo75 commented Oct 5, 2022

I actually found the cause of this today, but it's a bit of a job to fix it in master.

For your case @Ollinator - you will need to ditch the blueprint library. All of the calls use FLuaMachineModule::Get(), which returns the static singleton (this is why singletons aren't so great). This instance is shared between all of the execution state within an editor context - so if you launch editor, then attempt to launch multiple instances (as is the case with a client/server launch), they will be sharing the same lua context in memory.

I was able to get my use case working by creating a game instance subsystem and keeping track of my own lua states. Using this method also allows more than 1 instance of a particular LuaState class, since the UClass is being used to key the lua states in LuaMachine.

You can create LuaStates like this:
ULuaState* NewLuaState = NewObject<ULuaState>(TheSubsystemOrOtherUObject, LuaStateClass); NewLuaState.GetLuaState(GetWorld()); // This call is important, it sets up the initial global table and behaves like a lazy inititalizer.

Unfortunately, it doesn't solve the issue of the blueprint library using FLuaMachine::Get everywhere...

If the moderator decides to refactor this code, I'd highly recommend using Subsystems - it should solve most of these issues.

@Ollinator
Copy link
Author

Thx bfoo! Yeah, i imagined something like that would be needed.
For now, i'm fine with running in multiple processes; i've optimized everything i could so it launches within a couple of seconds, which isn't as fast as single process, but fast enough to be not slowed down dramatically...
Cheers!

@bfoo75
Copy link

bfoo75 commented Oct 7, 2022

Just in case anyone else comes across this thread - the method I described above has the side effect of not working with the lua machine debug tools, since they require the type-keyed map from LuaMachine to populate the states in the debug windows. :(

@rdeioris
Copy link
Owner

rdeioris commented Oct 9, 2022

I leave this issue opened as it contains lot of useful infos. It is even a boost for merging this long standing patch: #27

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants