-
Notifications
You must be signed in to change notification settings - Fork 33
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
Changes for better script online compatibility #369
Conversation
// Original function params: clone_heap(ThreadStorageContainer to, ThreadStorageContainer from) | ||
// HeapContainer has heap1 and heap2 variables, and some sort of timer, that just increases constantly, I guess to handle the rollback and multi-threaded stuff | ||
// The rest of what HeapContainer has is unknown for now | ||
// After writing to a chosen storage from the content of `from->heap1`, sets `to->heap2` to the newly copied thread storage |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this has to copy more than just state (like prng)
also if you only want callback then figuring out which thread is which from ThreadStorageContainer
would be enough, then you could just use detour instead of all of this asm
btw. I specially wrote not long ago patch_and_redirect
function, for exactly this kind of assembly hack, so you don't have to write all the boring jumps there and back and copy game code yourself xd
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah it also copies more stuff, but honestly I don't know how to handle that well in a callback, maybe make a struct for the whole storage that has prng, state, etc.
The issue with figuring out which thread is, is that I don't really know which thread is going to be the destination thread, at the start of the function you only know that to->heap2
pointer is gonna get overwritten at the with a pointer to the destination thread.
For getting to which ThreadStorage will be written to, the game code seems to do some lock and unlock stuff while looping through an array of the threads, which may affect the other threads, so I decided to not try to figure out to which one will be written to with my own code
I'll look into the patch_and_redirect
functions
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, I don't know if I can share some of the ghidra decompiled code to show it
Should I rename the PRE_CLONE_HEAP event now that there's the other events? (LOAD_STATE, SAVE_STATE) |
dbe6e4a
to
a91419c
Compare
script state is now in local_state_datas The deadlock was between a thread with g_all_backends_mutex and another with global_lua_lock
Yeah I think the best thing moving forward would be to just call it "state" (so CLONE_STATE or COPY_STATE), grow the existing struct to start at offset 0, renaming it to "State" in the api, absorbing whatever there is before the current StateMemory and possibly after (whatever is part of this heap), making sure the globals always point to the local versions... |
Would |
I meant grow the currently existing So
or
and
Now I don't exactly know what else is in the beginning of that new |
Those two points are mutually exclusive There is also a ton of stuff after I don't really see a reason for one giant struct, it will probably just mess with compile time or you will end up fighting the compiler alignments in it. For the x64dbg plugin i just made one function to get heap address and then you get offset for specific thing via enum |
Yes that's why I put "or" between them. Anyway what we're doing currently with the stuff before StateMemory (get StateMemory* and subtract some random offset) is absolutely bonkers compared to any alternative. |
I was going with the option of growing state, but not I noticed we have some game functions that use StateMemory* with the current size, like |
Well yeah clearly Even if we end up with just this, it's better than the magic number getters we currently have: struct State { // offset 0
// padding
PRNG prng;
uint32_t frame_count;
// padding
StateMemory state; // offset 0x4a0
}; Still open for suggestions for the naming of those structures, but internally it doesn't really matter that much. |
Wouldn't that require doing the same as before, substracting an offset to get the stuff in State?
Having stuff like |
I made most or all changes, though I had some doubts, I decided just to do the changes and then get it reviewed |
c364fa9
to
79c13f3
Compare
g_state = State::get().ptr_local(); | ||
if (g_state == nullptr) | ||
{ | ||
g_state = State::get().ptr_main(); | ||
} | ||
ScriptState& state = local_state_datas[g_state].state; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't backend constructed ASAP in playlunky? then this will always be main state anyway
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not sure about when they are constructed, are they when you create a new script in OL?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Was thinking more about PL
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah but this code is for both PL and OL, also handling what happens in OL makes sense to me
I have a local stash of changes to make timers and callbacks started from other callbacks to work online, by moving the callbacks Also, should I make |
Yeah maybe we should do this in smaller parts, but I'll say I have no intention of actually testing any of this online, just to make sure some single player mods still works right.
Makes sense to me too, even if just for testing. |
I removed the PRE_COPY_STATE for now because I don't really know what someone could use it for, so I prefer to leave it out until having something more clear |
g_SpawnTypeFlags is now SYSTEMIC by default because when some threads run spawns for the first times, the SYSTEMIC flag may not be set, causing desyncs online
On this PR I added |
/* StateMemory | ||
// user_data | ||
// You can store a table (or lua primitive) here and it will store data correctly in online multiplayer, by having a different copy on each state and being copied over when the game does. | ||
// Doesn't support recursive tables / cyclic references. Metatables will be transferred by reference instead of being copied |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Metatables will be transferred by reference instead of being copied
This is kind of obvious, since you just can't ever do a copy of metatable in lua
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
metatables are just tables, with fields that are accessed by lua when the table is set as a metatable of one, so those are copiable technically, but copying them doesn't make sense for what they are used most, if not all of the time (which is mostly making OOP like methods for tables). Not sure about metatables created by sol2.
https://www.lua.org/pil/13.html
I don't think that matters much. Just pick one |
do you mean to let anyone decide which to use, or to remove one of those? |
Aren't they exactly the same? it doesn't really matter which one we use. So just remove one. Also also, i plan to kill this function anyway, since we can just hardocde the offset 4A0 , it's not a big deal |
For now it only has a WIP code for the rollback function hook that copies the data from one thread to another, it works but I still have to make it better, add more changes and decide how to name the heap clone functions