From 98cb792975b1b69c33d826c2237158b1d7add2e1 Mon Sep 17 00:00:00 2001 From: Estebanfer Date: Sat, 3 Aug 2024 00:36:08 -0300 Subject: [PATCH] better deepcopy_object implementation --- docs/src/includes/_types.md | 2 +- src/game_api/script/lua_backend.cpp | 45 +++++++++++++++++++-- src/game_api/script/lua_vm.cpp | 10 ----- src/game_api/script/usertypes/state_lua.cpp | 3 +- 4 files changed, 44 insertions(+), 16 deletions(-) diff --git a/docs/src/includes/_types.md b/docs/src/includes/_types.md index ed81ad80d..d89d35a25 100644 --- a/docs/src/includes/_types.md +++ b/docs/src/includes/_types.md @@ -3115,7 +3115,7 @@ array<[THEME](#THEME), 9> | [journal_progress_theme_slots](https://github. [LiquidPhysics](#LiquidPhysics) | [liquid](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=liquid) | int | [next_entity_uid](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=next_entity_uid) | Next entity spawned will have this uid [RoomOwnersInfo](#RoomOwnersInfo) | [room_owners](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=room_owners) | Holds info about owned rooms and items (shops, challenge rooms, vault etc.) -any | [user_data](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=user_data) | You can put any arbitrary lua object here and it will work correctly in online multiplayer, by having a copy on each state and being copied when the game does.
+any | [user_data](https://github.com/spelunky-fyi/overlunky/search?l=Lua&q=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
## Texture types diff --git a/src/game_api/script/lua_backend.cpp b/src/game_api/script/lua_backend.cpp index 26d512a29..3bf95bd4e 100644 --- a/src/game_api/script/lua_backend.cpp +++ b/src/game_api/script/lua_backend.cpp @@ -6,9 +6,10 @@ #include // for format_error #include // for _List_iterator, _List_co... #include // for table_proxy, optional -#include // for stack -#include // for get -#include // for vector +#include +#include // for stack +#include // for get +#include // for vector #include "aliases.hpp" // for IMAGE, JournalPageType #include "bucket.hpp" // for Bucket @@ -1871,6 +1872,42 @@ void LuaBackend::on_post(ON event) } } +sol::table deepcopy_lua_table(sol::state& sol_state, sol::table& from_r) +{ + sol::table new_table(sol_state, sol::create); + for (auto& [k, v] : from_r.as()) + { + if (v.is()) + { + sol::table v_table = v.as(); + new_table.raw_set(k, deepcopy_lua_table(sol_state, v_table)); + } + else + { + new_table.raw_set(k, v); + } + } + auto maybe_metatable = from_r.raw_get>(sol::metatable_key); + if (maybe_metatable) + { + new_table.raw_set(sol::metatable_key, maybe_metatable.value()); + } + return new_table; +} + +inline sol::object deepcopy_lua(sol::state& sol_state, sol::object& from) +{ + if (from.is()) + { + auto from_t = from.as(); + return deepcopy_lua_table(sol_state, from_t); + } + else + { + return from; + } +} + void LuaBackend::copy_locals(StateMemory* from, StateMemory* to) { if (!local_state_datas.contains(from)) @@ -1882,7 +1919,7 @@ void LuaBackend::copy_locals(StateMemory* from, StateMemory* to) sol::object from_user_data = from_data.user_data; if (from_user_data != sol::lua_nil) { - to_data.user_data = (*vm)["deepcopy_object"](from_user_data); + to_data.user_data = deepcopy_lua(*vm, from_user_data); } } diff --git a/src/game_api/script/lua_vm.cpp b/src/game_api/script/lua_vm.cpp index bbd838c93..bce5d5172 100644 --- a/src/game_api/script/lua_vm.cpp +++ b/src/game_api/script/lua_vm.cpp @@ -2266,16 +2266,6 @@ end /// Initializes some seedeed run related values and loads the character select screen, as if starting a new seeded run after entering the seed. lua["play_seeded"] = init_seeded; - lua.script(R"##( - function deepcopy_object(obj) - if type(obj) ~= 'table' then return obj end - local res = {} - for k, v in pairs(obj) do res[deepcopy_object(k)] = deepcopy_object(v) end - res = setmetatable(res, getmetatable(obj)) - return res - end - )##"); - /// Change layer at which the liquid spawns in, THIS FUNCTION NEEDS TO BE CALLED BEFORE THE LEVEL IS BUILD, otherwise collisions and other stuff will be wrong for the newly spawned liquid /// This sadly also makes lavamanders extinct, since the logic for their spawn is harcoded to front layer with bunch of other unrelated stuff (you can still spawn them with script or place them directly in level files) /// Everything should be working more or less correctly (report on community discord if you find something unusual) diff --git a/src/game_api/script/usertypes/state_lua.cpp b/src/game_api/script/usertypes/state_lua.cpp index b2e6d3f09..1a90210a0 100644 --- a/src/game_api/script/usertypes/state_lua.cpp +++ b/src/game_api/script/usertypes/state_lua.cpp @@ -436,7 +436,8 @@ void register_usertypes(sol::state& lua) statememory_type["user_data"] = std::move(user_data); /* StateMemory // user_data - // You can put any arbitrary lua object here and it will work correctly in online multiplayer, by having a copy on each state and being copied when the game does. + // 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 */ lua.create_named_table("FADE", "NONE", 0, "OUT", 1, "LOAD", 2, "IN", 3);