more forcefully fix item duplication, but it should be fixed outright anyways, try to fix notplayer holding double wands, if player tries to pick a slot with no items, request items again, revert some enemy sync stuff to hopefully fix some entity duplication

This commit is contained in:
bgkillas 2024-11-24 10:39:15 -05:00
parent b8cb7eb998
commit 161e71fefc
7 changed files with 115 additions and 60 deletions

View file

@ -420,10 +420,10 @@ function inventory_helper.set_item_data(item_data, player_data, local_ent)
EntityAddComponent(item_entity, "LuaComponent", { EntityAddComponent(item_entity, "LuaComponent", {
script_throw_item = "mods/quant.ew/files/resource/cbs/throw_item.lua", script_throw_item = "mods/quant.ew/files/resource/cbs/throw_item.lua",
}) })
end local notify = EntityGetFirstComponentIncludingDisabled(item_entity, "LuaComponent", "ew_notify_component")
local notify = EntityGetFirstComponentIncludingDisabled(item_entity, "LuaComponent", "ew_notify_component") if notify ~= nil then
if notify ~= nil then EntityRemoveComponent(item_entity, notify)
EntityRemoveComponent(item_entity, notify) end
end end
--print("Deserialized wand #"..tostring(k).." - Active? "..tostring(wandInfo.active)) --print("Deserialized wand #"..tostring(k).." - Active? "..tostring(wandInfo.active))

View file

@ -644,7 +644,7 @@ function player_fns.set_current_slot(slot_data, player_data)
if (mActiveItem ~= item) then if (mActiveItem ~= item) then
np.SetActiveHeldEntity(player_data.entity, item, false, false) np.SetActiveHeldEntity(player_data.entity, item, false, false)
end end
return return true
end end
else else
print("something in inventory that is not an item") print("something in inventory that is not an item")

View file

@ -402,7 +402,7 @@ function util.get_phys_info(entity, kill)
local ret, info = pcall(serialize_phys_component, phys_component) local ret, info = pcall(serialize_phys_component, phys_component)
if not ret and kill then if not ret and kill then
EntityKill(entity) EntityKill(entity)
return nil return {{}, {}}
end end
table.insert(phys_info, info) table.insert(phys_info, info)
end end
@ -415,7 +415,7 @@ function util.get_phys_info(entity, kill)
local ret, info = pcall(serialize_phys_component, phys_component) local ret, info = pcall(serialize_phys_component, phys_component)
if not ret and kill then if not ret and kill then
EntityKill(entity) EntityKill(entity)
return nil return {{}, {}}
end end
table.insert(phys_info_2, info) table.insert(phys_info_2, info)
else else

View file

@ -49,8 +49,6 @@ local HpData = util.make_type({
f32 = {"hp", "max_hp"} f32 = {"hp", "max_hp"}
}) })
local wait_1_frame = false
local FULL_TURN = math.pi * 2 local FULL_TURN = math.pi * 2
local frame = 0 local frame = 0
@ -361,14 +359,7 @@ end
function enemy_sync.on_world_update_client() function enemy_sync.on_world_update_client()
if GameGetFrameNum() % 12 == 1 then if GameGetFrameNum() % 12 == 1 then
if wait_1_frame then enemy_sync.client_cleanup()
async(function()
wait(1)
enemy_sync.client_cleanup()
end)
else
enemy_sync.client_cleanup()
end
end end
if GameGetFrameNum() % (60*60) == 1 then if GameGetFrameNum() % (60*60) == 1 then
times_spawned_last_minute = {} times_spawned_last_minute = {}
@ -674,10 +665,7 @@ function rpc.handle_death_data(death_data)
end end
EntityInflictDamage(enemy_id, 1000000000, "DAMAGE_CURSE", "", "NONE", 0, 0, responsible_entity) -- Just to be sure EntityInflictDamage(enemy_id, 1000000000, "DAMAGE_CURSE", "", "NONE", 0, 0, responsible_entity) -- Just to be sure
async(function() EntityKill(enemy_id)
wait(1)
EntityKill(enemy_id)
end)
end end
::continue:: ::continue::
end end
@ -688,7 +676,6 @@ function rpc.handle_enemy_data(enemy_data)
for _, enemy_info_raw in ipairs(enemy_data) do for _, enemy_info_raw in ipairs(enemy_data) do
sync_enemy(enemy_info_raw, false) sync_enemy(enemy_info_raw, false)
end end
wait_1_frame = true
end end
function rpc.handle_enemy_health(enemy_health_data) function rpc.handle_enemy_health(enemy_health_data)

View file

@ -22,6 +22,8 @@ local frame = {}
local gid_last_frame_updated = {} local gid_last_frame_updated = {}
local wait_on_send = {}
function rpc.open_chest(gid) function rpc.open_chest(gid)
local ent = item_sync.find_by_gid(gid) local ent = item_sync.find_by_gid(gid)
if ent ~= nil then if ent ~= nil then
@ -46,7 +48,7 @@ end
util.add_cross_call("ew_chest_opened", function(chest_id) util.add_cross_call("ew_chest_opened", function(chest_id)
local gid = item_sync.get_global_item_id(chest_id) local gid = item_sync.get_global_item_id(chest_id)
if gid ~= "unknown" then if gid ~= nil then
rpc.open_chest(gid) rpc.open_chest(gid)
end end
end) end)
@ -97,10 +99,10 @@ end
function item_sync.get_global_item_id(item) function item_sync.get_global_item_id(item)
local gid = EntityGetFirstComponentIncludingDisabled(item, "VariableStorageComponent", "ew_global_item_id") local gid = EntityGetFirstComponentIncludingDisabled(item, "VariableStorageComponent", "ew_global_item_id")
if gid == nil then if gid == nil then
return "unknown" return nil
end end
local ret = ComponentGetValue2(gid, "value_string") local ret = ComponentGetValue2(gid, "value_string")
return ret or "unknown" return ret
end end
function item_sync.remove_item_with_id(gid) function item_sync.remove_item_with_id(gid)
@ -110,7 +112,8 @@ end
local find_by_gid_cache = {} local find_by_gid_cache = {}
function item_sync.find_by_gid(gid) function item_sync.find_by_gid(gid)
if find_by_gid_cache[gid] ~= nil then if find_by_gid_cache[gid] ~= nil then
if EntityGetIsAlive(find_by_gid_cache[gid]) and EntityHasTag(find_by_gid_cache[gid], "ew_global_item") then if EntityGetIsAlive(find_by_gid_cache[gid])
and EntityHasTag(find_by_gid_cache[gid], "ew_global_item") and is_item_on_ground(find_by_gid_cache[gid]) then
return find_by_gid_cache[gid] return find_by_gid_cache[gid]
else else
find_by_gid_cache[gid] = nil find_by_gid_cache[gid] = nil
@ -119,14 +122,19 @@ function item_sync.find_by_gid(gid)
--print("find_by_gid: searching") --print("find_by_gid: searching")
local global_items = EntityGetWithTag("ew_global_item") local candidate
for _, item in ipairs(global_items) do for _, item in ipairs(EntityGetWithTag("ew_global_item") or {}) do
local i_gid = item_sync.get_global_item_id(item) local i_gid = item_sync.get_global_item_id(item)
find_by_gid_cache[i_gid] = item find_by_gid_cache[i_gid] = item
if i_gid == gid then if i_gid == gid then
return item if is_item_on_ground(item) then
return item
else
candidate = item
end
end end
end end
return candidate
end end
function item_sync.remove_item_with_id_now(gid) function item_sync.remove_item_with_id_now(gid)
@ -163,6 +171,11 @@ function item_sync.host_localize_item(gid, peer_id)
item_sync.remove_item_with_id(gid) item_sync.remove_item_with_id(gid)
end end
rpc.item_localize(peer_id, gid) rpc.item_localize(peer_id, gid)
if peer_id == ctx.my_id then
item_sync.take_authority(gid)
else
rpc.hand_authority_over_to(peer_id, gid)
end
end end
local wait_for_gid = {} local wait_for_gid = {}
@ -205,6 +218,7 @@ local function make_global(item, give_authority_to)
ctx.item_prevent_localize[gid] = false ctx.item_prevent_localize[gid] = false
rpc.item_globalize(item_data) rpc.item_globalize(item_data)
wait_on_send[gid] = nil
end end
function item_sync.make_item_global(item, instant, give_authority_to) function item_sync.make_item_global(item, instant, give_authority_to)
@ -238,6 +252,10 @@ local function remove_client_items_from_world()
end end
end end
local function is_peers_item(gid, peer)
return string.sub(gid, 1, 16) == peer
end
local function is_my_item(gid) local function is_my_item(gid)
return string.sub(gid, 1, 16) == ctx.my_id return string.sub(gid, 1, 16) == ctx.my_id
end end
@ -253,7 +271,9 @@ rpc.opts_everywhere()
rpc.opts_reliable() rpc.opts_reliable()
function rpc.give_authority_to(gid, new_id) function rpc.give_authority_to(gid, new_id)
local item = item_sync.find_by_gid(gid) local item = item_sync.find_by_gid(gid)
find_by_gid_cache[gid] = nil
if item ~= nil then if item ~= nil then
find_by_gid_cache[new_id] = item
local var = EntityGetFirstComponentIncludingDisabled(item, "VariableStorageComponent", "ew_global_item_id") local var = EntityGetFirstComponentIncludingDisabled(item, "VariableStorageComponent", "ew_global_item_id")
ComponentSetValue2(var, "value_string", new_id) ComponentSetValue2(var, "value_string", new_id)
end end
@ -316,10 +336,7 @@ function rpc.handle_death_data(death_data)
end end
EntityInflictDamage(enemy_id, 1000000000, "DAMAGE_CURSE", "", "NONE", 0, 0, responsible_entity) -- Just to be sure EntityInflictDamage(enemy_id, 1000000000, "DAMAGE_CURSE", "", "NONE", 0, 0, responsible_entity) -- Just to be sure
async(function() EntityKill(enemy_id)
wait(1)
EntityKill(enemy_id)
end)
end end
::continue:: ::continue::
end end
@ -327,16 +344,18 @@ end
local DISTANCE_LIMIT = 128 * 4 local DISTANCE_LIMIT = 128 * 4
local ignore = {}
local function send_item_positions(all) local function send_item_positions(all)
local position_data = {} local position_data = {}
local cx, cy = EntityGetTransform(ctx.my_player.entity) local cx, cy = EntityGetTransform(ctx.my_player.entity)
for _, item in ipairs(EntityGetWithTag("ew_global_item")) do for _, item in ipairs(EntityGetWithTag("ew_global_item")) do
local gid = item_sync.get_global_item_id(item) local gid = item_sync.get_global_item_id(item)
-- Only send info about items created by us. -- Only send info about items created by us.
if is_my_item(gid) and is_item_on_ground(item) then if gid ~= nil and is_my_item(gid) and is_item_on_ground(item) then
local x, y = EntityGetTransform(item) local x, y = EntityGetTransform(item)
local dx, dy = x - cx, y - cy local dx, dy = x - cx, y - cy
if dx * dx + dy * dy > 1024 * 1024 then if (ignore[gid] == nil or ignore[gid] < GameGetFrameNum()) and dx * dx + dy * dy > 4 * DISTANCE_LIMIT * DISTANCE_LIMIT then
local ent = EntityGetClosestWithTag(x, y, "ew_peer") local ent = EntityGetClosestWithTag(x, y, "ew_peer")
local nx, ny local nx, ny
local ndx, ndy local ndx, ndy
@ -351,6 +370,7 @@ local function send_item_positions(all)
ndx, ndy = x - nx, y - ny ndx, ndy = x - nx, y - ny
end end
if ent == 0 or ndx * ndx + ndy * ndy > DISTANCE_LIMIT * DISTANCE_LIMIT then if ent == 0 or ndx * ndx + ndy * ndy > DISTANCE_LIMIT * DISTANCE_LIMIT then
ignore[gid] = GameGetFrameNum() + 60
goto continue goto continue
end end
end end
@ -358,11 +378,14 @@ local function send_item_positions(all)
if data ~= nil then if data ~= nil then
local peer = data.peer_id local peer = data.peer_id
rpc.hand_authority_over_to(peer, gid) rpc.hand_authority_over_to(peer, gid)
ignore[gid] = nil
else
ignore[gid] = GameGetFrameNum() + 60
end end
else else
local phys_info = util.get_phys_info(item, true) local phys_info = util.get_phys_info(item, true)
if ((phys_info[1] ~= nil and phys_info[1][1] ~= nil) if (phys_info[1][1] ~= nil
or (phys_info[2] ~= nil and phys_info[2][1] ~= nil) or phys_info[2][1] ~= nil
or all) or all)
and (#EntityGetInRadiusWithTag(x, y, DISTANCE_LIMIT, "ew_peer") ~= 0 and (#EntityGetInRadiusWithTag(x, y, DISTANCE_LIMIT, "ew_peer") ~= 0
or #EntityGetInRadiusWithTag(x, y, DISTANCE_LIMIT, "polymorphed_player") ~= 0) then or #EntityGetInRadiusWithTag(x, y, DISTANCE_LIMIT, "polymorphed_player") ~= 0) then
@ -371,7 +394,7 @@ local function send_item_positions(all)
if costcom ~= nil then if costcom ~= nil then
cost = ComponentGetValue2(costcom, "cost") cost = ComponentGetValue2(costcom, "cost")
local mx, my = GameGetCameraPos() local mx, my = GameGetCameraPos()
if math.abs(mx - x) < 1024 and math.abs(my - y) < 1024 if math.abs(mx - x) < DISTANCE_LIMIT * 2 and math.abs(my - y) < DISTANCE_LIMIT * 2
and EntityGetFirstComponentIncludingDisabled(item, "VariableStorageComponent", "ew_try_stealable") then and EntityGetFirstComponentIncludingDisabled(item, "VariableStorageComponent", "ew_try_stealable") then
ComponentSetValue2(costcom, "stealable", true) ComponentSetValue2(costcom, "stealable", true)
end end
@ -399,11 +422,9 @@ function item_sync.on_world_update_host()
item_sync.make_item_global(thrown_item) item_sync.make_item_global(thrown_item)
end end
local picked_item = get_global_ent("ew_picked") local picked_item = get_global_ent("ew_picked")
if picked_item ~= nil and EntityHasTag(picked_item, "ew_global_item") if picked_item ~= nil and EntityHasTag(picked_item, "ew_global_item") then
and EntityHasTag(EntityGetRootEntity(picked_item), "ew_peer") then
local gid = item_sync.get_global_item_id(picked_item) local gid = item_sync.get_global_item_id(picked_item)
item_sync.host_localize_item(gid, ctx.my_id) item_sync.host_localize_item(gid, ctx.my_id)
item_sync.take_authority(gid)
end end
remove_client_items_from_world() remove_client_items_from_world()
end end
@ -419,11 +440,9 @@ function item_sync.on_world_update_client()
end end
local picked_item = get_global_ent("ew_picked") local picked_item = get_global_ent("ew_picked")
if picked_item ~= nil and EntityHasTag(picked_item, "ew_global_item") if picked_item ~= nil and EntityHasTag(picked_item, "ew_global_item") then
and EntityHasTag(EntityGetRootEntity(picked_item), "ew_peer") then
local gid = item_sync.get_global_item_id(picked_item) local gid = item_sync.get_global_item_id(picked_item)
rpc.item_localize_req(gid) rpc.item_localize_req(gid)
item_sync.take_authority(gid)
end end
remove_client_items_from_world() remove_client_items_from_world()
end end
@ -471,9 +490,8 @@ function item_sync.on_should_send_updates()
if not ctx.is_host then if not ctx.is_host then
return return
end end
local global_items = EntityGetWithTag("ew_global_item")
local item_list = {} local item_list = {}
for _, item in ipairs(global_items) do for _, item in ipairs(EntityGetWithTag("ew_global_item") or {}) do
if is_item_on_ground(item) and not EntityHasTag(item, "mimic_potion") then if is_item_on_ground(item) and not EntityHasTag(item, "mimic_potion") then
local item_data = inventory_helper.serialize_single_item(item) local item_data = inventory_helper.serialize_single_item(item)
local gid = item_sync.get_global_item_id(item) local gid = item_sync.get_global_item_id(item)
@ -548,7 +566,7 @@ function rpc.item_globalize(item_data)
item_sync.remove_item_with_id_now(item_data.gid) item_sync.remove_item_with_id_now(item_data.gid)
end end
local n = item_sync.find_by_gid(item_data.gid) local n = item_sync.find_by_gid(item_data.gid)
if n ~= nil and EntityGetRootEntity(n) == n then if n ~= nil then
return return
end end
local item = inventory_helper.deserialize_single_item(item_data) local item = inventory_helper.deserialize_single_item(item_data)
@ -593,10 +611,24 @@ local function cleanup(peer)
local item = item_sync.find_by_gid(gid) local item = item_sync.find_by_gid(gid)
if is_item_on_ground(item) then if is_item_on_ground(item) then
EntityKill(item) EntityKill(item)
gid_last_frame_updated[peer][gid] = nil
end
end
end
local is_duplicate = {}
for _, item in ipairs(EntityGetWithTag("ew_global_item") or {}) do
local gid = item_sync.get_global_item_id(item)
if gid ~= nil and is_peers_item(gid, peer) then
if (gid_last_frame_updated[peer] == nil or is_duplicate[gid]) and is_item_on_ground(item) then
EntityKill(item)
else
if is_duplicate[gid] then
EntityKill(is_duplicate[gid])
end
is_duplicate[gid] = item
end end
end end
end end
gid_last_frame_updated[peer] = {}
end end
function rpc.update_positions(position_data, all) function rpc.update_positions(position_data, all)
@ -628,20 +660,15 @@ function rpc.update_positions(position_data, all)
ComponentSetValue2(costcom, "cost", price) ComponentSetValue2(costcom, "cost", price)
end end
end end
else elseif wait_for_gid[gid] == nil then
util.log("Requesting again "..gid) util.log("Requesting again "..gid)
if wait_for_gid[gid] == nil then rpc.request_send_again(gid)
rpc.request_send_again(gid) wait_for_gid[gid] = GameGetFrameNum() + 300
wait_for_gid[gid] = GameGetFrameNum() + 300
end
end end
end end
end end
if all then if all then
async(function() cleanup(ctx.rpc_peer_id)
wait(1)
cleanup(ctx.rpc_peer_id)
end)
end end
end end
@ -654,7 +681,10 @@ function rpc.request_send_again(gid)
util.log("Requested to send item again, but this item wasn't found: "..gid) util.log("Requested to send item again, but this item wasn't found: "..gid)
return return
end end
item_sync.make_item_global(item) if wait_on_send[gid] == nil or wait_on_send[gid] < GameGetFrameNum() then
wait_on_send[gid] = GameGetFrameNum() + 240
item_sync.make_item_global(item)
end
end end
ctx.cap.item_sync = { ctx.cap.item_sync = {

View file

@ -205,6 +205,22 @@ local function show_death_message()
end end
end end
local function reset_cast_state_if_has_any_other_item(player_data)
local inventory2Comp = EntityGetFirstComponentIncludingDisabled(player_data.entity, "Inventory2Component")
if inventory2Comp == nil then
return
end
local mActiveItem = ComponentGetValue2(inventory2Comp, "mActiveItem")
for k, item in ipairs(inventory_helper.get_inventory_items(player_data, "inventory_quick") or {}) do
if item ~= mActiveItem then
np.SetActiveHeldEntity(player_data.entity, item)
np.SetActiveHeldEntity(player_data.entity, mActiveItem)
break
end
end
end
local function player_died() local function player_died()
if ctx.my_player.entity == nil then if ctx.my_player.entity == nil then
return return
@ -275,12 +291,13 @@ local function player_died()
polymorph.switch_entity(ent + 1) polymorph.switch_entity(ent + 1)
remove_healthbar_locally() remove_healthbar_locally()
inventory_helper.set_item_data(item_data, ctx.my_player, true)
for _, child in ipairs(EntityGetAllChildren(ctx.my_player.entity) or {}) do for _, child in ipairs(EntityGetAllChildren(ctx.my_player.entity) or {}) do
if EntityGetName(child) == "cursor" or EntityGetName(child) == "notcursor" then if EntityGetName(child) == "cursor" or EntityGetName(child) == "notcursor" then
EntitySetComponentIsEnabled(child, EntityGetFirstComponentIncludingDisabled(child, "SpriteComponent"), false) EntitySetComponentIsEnabled(child, EntityGetFirstComponentIncludingDisabled(child, "SpriteComponent"), false)
end end
end end
inventory_helper.set_item_data(item_data, ctx.my_player, true) reset_cast_state_if_has_any_other_item(ctx.my_player)
remove_inventory_tags() remove_inventory_tags()
perk_fns.update_perks_for_entity(perk_data, ctx.my_player.entity, allow_notplayer_perk) perk_fns.update_perks_for_entity(perk_data, ctx.my_player.entity, allow_notplayer_perk)
util.set_ent_health(ctx.my_player.entity, {max_hp, max_hp}) util.set_ent_health(ctx.my_player.entity, {max_hp, max_hp})

View file

@ -18,6 +18,22 @@ function rpc.send_money_and_ingestion(money, ingestion_size)
end end
end end
local wait_on_send = 0
function rpc.request_items(peer_id)
if ctx.my_id == peer_id
and wait_on_send < GameGetFrameNum() then
wait_on_send = GameGetFrameNum() + 240
inventory_helper.has_inventory_changed(ctx.my_player)
local inventory_state = player_fns.serialize_items(ctx.my_player)
if inventory_state ~= nil then
net.send_player_inventory(inventory_state)
end
end
end
local wait_on_requst = {}
function rpc.player_update(input_data, pos_data, phys_info, current_slot, team) function rpc.player_update(input_data, pos_data, phys_info, current_slot, team)
local peer_id = ctx.rpc_peer_id local peer_id = ctx.rpc_peer_id
@ -43,7 +59,12 @@ function rpc.player_update(input_data, pos_data, phys_info, current_slot, team)
player_fns.deserialize_position(pos_data, phys_info, player_data) player_fns.deserialize_position(pos_data, phys_info, player_data)
end end
if current_slot ~= nil then if current_slot ~= nil then
player_fns.set_current_slot(current_slot, player_data) if not player_fns.set_current_slot(current_slot, player_data)
and (wait_on_requst[player_data.peer_id] == nil or wait_on_requst[player_data.peer_id] < GameGetFrameNum()) then
print("slot empty, requesting items")
wait_on_requst[player_data.peer_id] = GameGetFrameNum() + 300
rpc.request_items(player_data.peer_id)
end
end end
end end