diff --git a/mods/ENTITIES/mcl_minecarts/carts.lua b/mods/ENTITIES/mcl_minecarts/carts.lua index f0e4fab65..73f106201 100644 --- a/mods/ENTITIES/mcl_minecarts/carts.lua +++ b/mods/ENTITIES/mcl_minecarts/carts.lua @@ -6,11 +6,13 @@ local S = minetest.get_translator(modname) local mcl_log = mcl_util.make_mcl_logger("mcl_logging_minecarts", "Minecarts") -- Imports +local CART_BLOCK_SIZE = mod.CART_BLOCK_SIZE local table_merge = mcl_util.table_merge local get_cart_data = mod.get_cart_data local save_cart_data = mod.save_cart_data local update_cart_data = mod.update_cart_data local destroy_cart_data = mod.destroy_cart_data +local find_carts_by_block_map = mod.find_carts_by_block_map local do_movement,do_detached_movement,handle_cart_enter = dofile(modpath.."/movement.lua") assert(do_movement) assert(do_detached_movement) @@ -165,9 +167,15 @@ function DEFAULT_CART_DEF:on_step(dtime) self._staticdata = staticdata end + -- Repair cart_type + if not staticdata.cart_type then + staticdata.cart_type = self.name + end + if self._seq ~= staticdata.seq then - print("TODO: remove cart #"..staticdata.uuid.." with sequence number mismatch") - print("self.seq="..tostring(self._seq)..", staticdata.seq="..tostring(staticdata.seq)) + print("removing cart #"..staticdata.uuid.." with sequence number mismatch") + self.object:remove() + return end -- Regen @@ -309,6 +317,7 @@ function mcl_minecarts.place_minecart(itemstack, pointed_thing, placer) local uuid = mcl_util.get_uuid(cart) data = make_staticdata( railtype, railpos, cart_dir ) data.uuid = uuid + data.cart_type = entity_id update_cart_data(data) le._staticdata = data save_cart_data(le._staticdata.uuid) @@ -447,7 +456,67 @@ if minetest.get_modpath("mcl_wip") then mcl_wip.register_wip_item("mcl_minecarts:command_block_minecart") end +local function respawn_cart(cart) + local cart_type = cart.cart_type or "mcl_minecarts:minecart" + local pos = mod.get_cart_position(cart) + print("Respawning cart #"..cart.uuid.." at "..tostring(pos)) + + -- Update sequence so that old cart entities get removed + cart.seq = (cart.seq or 1) + 1 + save_cart_data(cart.uuid) + + -- Create the new entity + local entity = minetest.add_entity(pos, cart_type) + local le = entity:get_luaentity() + le._seq = cart.seq + le._uuid = cart.uuid + le._staticdata = cart + + -- We intentionally don't call the normal hooks because this minecart was already there +end + +-- Try to respawn cart entities for carts that have moved into range of a player +local function try_respawn_carts() + -- Build a map of blocks near players + local block_map = {} + local players = minetest.get_connected_players() + for _,player in pairs(players) do + local pos = player:get_pos() + local min = vector.floor(vector.divide(vector.offset(pos,-CART_BLOCK_SIZE,-CART_BLOCK_SIZE,-CART_BLOCK_SIZE), CART_BLOCK_SIZE)) + local max = vector.floor(vector.divide(vector.offset(pos, CART_BLOCK_SIZE, CART_BLOCK_SIZE, CART_BLOCK_SIZE), CART_BLOCK_SIZE)) + vector.new(1,1,1) + for z = min.z,max.z do + for y = min.y,max.y do + for x = min.x,min.x do + block_map[ vector.to_string(vector.new(x,y,z)) ] = true + end + end + end + end + + -- Find all cart data that are in these blocks + local carts = find_carts_by_block_map(block_map) + + -- Check to see if any of these don't have an entity + for _,cart in pairs(carts) do + local le = mcl_util.get_luaentity_from_uuid(cart.uuid) + if not le then + respawn_cart(cart) + end + end +end + +local timer = 0 minetest.register_globalstep(function(dtime) + timer = timer - dtime + if timer <= 0 then + local start_time = minetest.get_us_time() + try_respawn_carts() + local stop_time = minetest.get_us_time() + local duration = (stop_time - start_time) / 1e6 + timer = duration / 50e-6 -- Schedule 50us per second + --print("Took "..tostring(duration).." seconds, rescheduling for "..tostring(timer).." seconds in the future") + end + -- TODO: handle periodically updating out-of-range carts end) diff --git a/mods/ENTITIES/mcl_minecarts/functions.lua b/mods/ENTITIES/mcl_minecarts/functions.lua index 6b8d067bc..53cc6b74c 100644 --- a/mods/ENTITIES/mcl_minecarts/functions.lua +++ b/mods/ENTITIES/mcl_minecarts/functions.lua @@ -356,4 +356,10 @@ function mcl_minecarts:update_cart_orientation() self.object:set_rotation(rot) end +function mod.get_cart_position(cart_staticdata) + local data = cart_staticdata + if not data.connected_at then return end + + return vector.add(data.connected_at, vector.multiply(data.dir or vector.zero(), data.distance or 0)) +end diff --git a/mods/ENTITIES/mcl_minecarts/init.lua b/mods/ENTITIES/mcl_minecarts/init.lua index 22c67de5b..55f348544 100644 --- a/mods/ENTITIES/mcl_minecarts/init.lua +++ b/mods/ENTITIES/mcl_minecarts/init.lua @@ -9,6 +9,7 @@ mod.speed_max = 10 mod.check_float_time = 15 mod.FRICTION = 0.4 mod.MAX_TRAIN_LENGTH = 4 +mod.CART_BLOCK_SIZE = 64 for _,filename in pairs({"storage","functions","rails","train","carts"}) do dofile(modpath.."/"..filename..".lua") diff --git a/mods/ENTITIES/mcl_minecarts/storage.lua b/mods/ENTITIES/mcl_minecarts/storage.lua index f4ee1af51..adc7f23a6 100644 --- a/mods/ENTITIES/mcl_minecarts/storage.lua +++ b/mods/ENTITIES/mcl_minecarts/storage.lua @@ -1,10 +1,14 @@ local storage = minetest.get_mod_storage() local mod = mcl_minecarts +-- Imports +local CART_BLOCK_SIZE = mod.CART_BLOCK_SIZE + local cart_data = {} local cart_data_fail_cache = {} +local cart_ids = storage:get_keys() -function mod.get_cart_data(uuid) +local function get_cart_data(uuid) if cart_data[uuid] then return cart_data[uuid] end if cart_data_fail_cache[uuid] then return nil end @@ -17,6 +21,14 @@ function mod.get_cart_data(uuid) cart_data[uuid] = data return data end +mod.get_cart_data = get_cart_data + +-- Preload all cart data into memory +for _,id in pairs(cart_ids) do + local uuid = string.sub(id,6) + get_cart_data(uuid) +end + local function save_cart_data(uuid) if not cart_data[uuid] then return end storage:set_string("cart-"..uuid,minetest.serialize(cart_data[uuid])) @@ -35,3 +47,17 @@ function mod.destroy_cart_data(uuid) cart_data_fail_cache[uuid] = true end +function mod.find_carts_by_block_map(block_map) + local cart_list = {} + for _,data in pairs(cart_data) do + if data and data.connected_at then + local pos = mod.get_cart_position(data) + local block = vector.floor(vector.divide(pos,CART_BLOCK_SIZE)) + if block_map[vector.to_string(block)] then + cart_list[#cart_list + 1] = data + end + end + end + return cart_list +end +