forked from VoxeLibre/VoxeLibre
Add utilities to convert between an ObjectRef, it's active object id and a 128bit uuid, move minecart data from entity staticdata to mod storage to eventually allow updating carts when out of range of players and also track what carts are alive, implement on-rail cart collisions
This commit is contained in:
parent
233e253a5d
commit
b815253704
|
@ -1179,3 +1179,49 @@ function mcl_util.table_keys(t)
|
|||
return keys
|
||||
end
|
||||
|
||||
local uuid_to_aoid_cache = {}
|
||||
local function scan_active_objects()
|
||||
-- Update active object ids for all active objects
|
||||
for active_object_id,o in pairs(minetest.luaentities) do
|
||||
o._active_object_id = active_object_id
|
||||
if o._uuid then
|
||||
uuid_to_aoid_cache[o._uuid] = active_object_id
|
||||
end
|
||||
end
|
||||
end
|
||||
function mcl_util.get_active_object_id(obj)
|
||||
local le = obj:get_luaentity()
|
||||
|
||||
-- If the active object id in the lua entity is correct, return that
|
||||
if le._active_object_id and minetest.luaentities[le._active_object_id] == le then
|
||||
return le._active_object_id
|
||||
end
|
||||
|
||||
scan_active_objects()
|
||||
|
||||
return le._active_object_id
|
||||
end
|
||||
function mcl_util.get_active_object_id_from_uuid(uuid)
|
||||
return uuid_to_aoid_cache[uuid] or scan_active_objects() or uuid_to_aoid_cache[uuid]
|
||||
end
|
||||
function mcl_util.get_uuid(obj)
|
||||
local le = obj:get_luaentity()
|
||||
|
||||
if le._uuid then return le._uuid end
|
||||
|
||||
-- Generate a random 128-bit ID that can be assumed to be unique
|
||||
-- To have a 1% chance of a collision, there would have to be 1.6x10^76 IDs generated
|
||||
-- https://en.wikipedia.org/wiki/Birthday_problem#Probability_table
|
||||
local u = {}
|
||||
for i = 1,16 do
|
||||
u[#u + 1] = string.format("%02X",math.random(1,255))
|
||||
end
|
||||
le._uuid = table.concat(u)
|
||||
|
||||
-- Update the cache with this new id
|
||||
aoid = mcl_util.get_active_object_id(obj)
|
||||
uuid_to_aoid_cache[le._uuid] = aoid
|
||||
|
||||
return le._uuid
|
||||
end
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
local modname = minetest.get_current_modname()
|
||||
local mod = mcl_minecarts
|
||||
local storage = minetest.get_mod_storage()
|
||||
local S = minetest.get_translator(modname)
|
||||
|
||||
local LOGGING_ON = minetest.settings:get_bool("mcl_logging_minecarts", false)
|
||||
|
@ -19,6 +20,32 @@ local friction = 0.4
|
|||
local MINECART_MAX_HP = 4
|
||||
local PASSENGER_ATTACH_POSITION = vector.new(0, -1.75, 0)
|
||||
|
||||
local cart_data = {}
|
||||
local cart_data_fail_cache = {}
|
||||
|
||||
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
|
||||
|
||||
local data = minetest.deserialize(storage:get_string("cart-"..uuid))
|
||||
if not data then
|
||||
cart_data_fail_cache[uuid] = true
|
||||
return nil
|
||||
end
|
||||
|
||||
cart_data[uuid] = data
|
||||
return data
|
||||
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]))
|
||||
end
|
||||
local function destroy_cart_data(uuid)
|
||||
storage:set_string("cart-"..uuid,"")
|
||||
cart_data[uuid] = nil
|
||||
cart_data_fail_cache[uuid] = true
|
||||
end
|
||||
|
||||
local function detach_minecart(self)
|
||||
local staticdata = self._staticdata
|
||||
|
||||
|
@ -72,13 +99,68 @@ local function handle_cart_enter_exit(self, pos, next_dir, event)
|
|||
local hook = self["_mcl_minecarts_"..event]
|
||||
if hook then hook(self, pos) end
|
||||
end
|
||||
local function set_metadata_cart_status(pos, uuid, state)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local carts = minetest.deserialize(meta:get_string("_mcl_minecarts_carts")) or {}
|
||||
carts[uuid] = state
|
||||
meta:set_string("_mcl_minecarts_carts", minetest.serialize(carts))
|
||||
end
|
||||
local function handle_cart_enter(self, pos, next_dir)
|
||||
--print("entering "..tostring(pos))
|
||||
set_metadata_cart_status(pos, self._staticdata.uuid, 1)
|
||||
handle_cart_enter_exit(self, pos, next_dir, "on_enter" )
|
||||
end
|
||||
local function handle_cart_leave(self, pos, next_dir)
|
||||
--print("leaving "..tostring(pos))
|
||||
set_metadata_cart_status(pos, self._staticdata.uuid, nil)
|
||||
handle_cart_enter_exit(self, pos, next_dir, "on_leave" )
|
||||
end
|
||||
|
||||
local function handle_cart_collision(cart1, pos, next_dir)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local carts = minetest.deserialize(meta:get_string("_mcl_minecarts_carts")) or {}
|
||||
local cart_uuid = nil
|
||||
local dirty = false
|
||||
for uuid,v in pairs(carts) do
|
||||
-- Clean up dead carts
|
||||
if not get_cart_data(uuid) then
|
||||
carts[uuid] = nil
|
||||
dirty = true
|
||||
uuid = nil
|
||||
end
|
||||
|
||||
if uuid and uuid ~= cart1._staticdata.uuid then cart_uuid = uuid end
|
||||
end
|
||||
if dirty then
|
||||
meta:set_string("_mcl_minecarts_carts",minetest.serialize(carts))
|
||||
end
|
||||
|
||||
local meta = minetest.get_meta(vector.add(pos,next_dir))
|
||||
if not cart_uuid then return end
|
||||
minetest.log("action","cart #"..cart1._staticdata.uuid.." collided with cart #"..cart_uuid.." at "..tostring(pos))
|
||||
|
||||
local cart2_aoid = mcl_util.get_active_object_id_from_uuid(cart_uuid)
|
||||
local cart2 = minetest.luaentities[cart2_aoid]
|
||||
if not cart2 then return end
|
||||
|
||||
local cart1_staticdata = cart1._staticdata
|
||||
local cart2_staticdata = cart2._staticdata
|
||||
|
||||
local u1 = cart1_staticdata.velocity
|
||||
local u2 = cart2_staticdata.velocity
|
||||
local m1 = cart1_staticdata.mass
|
||||
local m2 = cart2_staticdata.mass
|
||||
|
||||
-- Calculate new velocities according to https://en.wikipedia.org/wiki/Elastic_collision#One-dimensional_Newtonian
|
||||
local c1 = m1 + m2
|
||||
local d = m1 - m2
|
||||
local v1 = ( d * u1 + 2 * m2 * u2 ) / c1
|
||||
local v2 = ( 2 * m1 * u1 + d * u2 ) / c1
|
||||
|
||||
cart1_staticdata.velocity = v1
|
||||
cart2_staticdata.velocity = v2
|
||||
end
|
||||
|
||||
local function handle_cart_node_watches(self, dtime)
|
||||
local staticdata = self._staticdata
|
||||
local watches = staticdata.node_watches or {}
|
||||
|
@ -246,7 +328,7 @@ local function do_movement_step(self, dtime)
|
|||
end
|
||||
|
||||
if DEBUG and ( v_0 > 0 or a ~= 0 ) then
|
||||
print( " cart #"..tostring(staticdata.cart_id)..
|
||||
print( " cart "..tostring(staticdata.uuid)..
|
||||
": a="..tostring(a)..
|
||||
",v_0="..tostring(v_0)..
|
||||
",x_0="..tostring(x_0)..
|
||||
|
@ -305,7 +387,7 @@ local function do_movement_step(self, dtime)
|
|||
staticdata.distance = x_1
|
||||
|
||||
if DEBUG and (1==0) and ( v_0 > 0 or a ~= 0 ) then
|
||||
print( "- cart #"..tostring(staticdata.cart_id)..
|
||||
print( "- cart #"..tostring(staticdata.uuid)..
|
||||
": a="..tostring(a)..
|
||||
",v_0="..tostring(v_0)..
|
||||
",v_1="..tostring(v_1)..
|
||||
|
@ -337,6 +419,9 @@ local function do_movement_step(self, dtime)
|
|||
print( "Changing direction from "..tostring(staticdata.dir).." to "..tostring(next_dir))
|
||||
end
|
||||
|
||||
-- Handle cart collisions
|
||||
handle_cart_collision(self, pos, next_dir)
|
||||
|
||||
-- Leave the old node
|
||||
handle_cart_leave(self, old_pos, next_dir )
|
||||
|
||||
|
@ -376,7 +461,7 @@ local function do_movement_step(self, dtime)
|
|||
|
||||
-- Debug reporting
|
||||
if DEBUG and ( v_0 > 0 or v_1 > 0 ) then
|
||||
print( " cart #"..tostring(staticdata.cart_id)..
|
||||
print( " cart #"..tostring(staticdata.uuid)..
|
||||
": a="..tostring(a)..
|
||||
",v_0="..tostring(v_0)..
|
||||
",v_1="..tostring(v_1)..
|
||||
|
@ -615,7 +700,7 @@ local function make_staticdata( railtype, connected_at, dir )
|
|||
distance = 0,
|
||||
velocity = 0,
|
||||
dir = vector.new(dir),
|
||||
cart_id = math.random(1,1000000000),
|
||||
mass = 1,
|
||||
}
|
||||
end
|
||||
|
||||
|
@ -644,24 +729,40 @@ local DEFAULT_CART_DEF = {
|
|||
_staticdata = nil,
|
||||
}
|
||||
function DEFAULT_CART_DEF:on_activate(staticdata, dtime_s)
|
||||
-- Transfer older data
|
||||
local data = minetest.deserialize(staticdata) or {}
|
||||
if not data.uuid then
|
||||
data.uuid = mcl_util.get_uuid(self.object)
|
||||
end
|
||||
local cd = get_cart_data(data.uuid)
|
||||
if not cd then
|
||||
cart_data[data.uuid] = data
|
||||
cart_data_fail_cache[data.uuid] = nil
|
||||
save_cart_data(data.uuid)
|
||||
end
|
||||
|
||||
-- Initialize
|
||||
local data = minetest.deserialize(staticdata)
|
||||
if type(data) == "table" then
|
||||
-- Migrate old data
|
||||
if data._railtype then
|
||||
data.railtype = data._railtype
|
||||
data._railtype = nil
|
||||
end
|
||||
|
||||
-- Fix up types
|
||||
data.dir = vector.new(data.dir)
|
||||
|
||||
-- Fix mass
|
||||
data.mass = data.mass or 1
|
||||
|
||||
-- Make sure all carts have an ID to isolate them
|
||||
data.cart_id = staticdata.cart_id or math.random(1,1000000000)
|
||||
self._uuid = data.uuid
|
||||
data.uuid = mcl_util.get_uuid(self.object)
|
||||
|
||||
self._staticdata = data
|
||||
end
|
||||
|
||||
-- Activate cart if on activator rail
|
||||
-- Activate cart if on powered activator rail
|
||||
if self.on_activate_by_rail then
|
||||
local pos = self.object:get_pos()
|
||||
local node = minetest.get_node(vector.floor(pos))
|
||||
|
@ -670,6 +771,11 @@ function DEFAULT_CART_DEF:on_activate(staticdata, dtime_s)
|
|||
end
|
||||
end
|
||||
end
|
||||
function DEFAULT_CART_DEF:get_staticdata()
|
||||
save_cart_data(self._staticdata.uuid)
|
||||
return minetest.serialize({uuid = self._staticdata.uuid})
|
||||
end
|
||||
|
||||
function DEFAULT_CART_DEF:add_node_watch(pos)
|
||||
local staticdata = self._staticdata
|
||||
local watches = staticdata.node_watches or {}
|
||||
|
@ -693,9 +799,6 @@ function DEFAULT_CART_DEF:remove_node_watch(pos)
|
|||
end
|
||||
staticdata.node_watches = new_watches
|
||||
end
|
||||
function DEFAULT_CART_DEF:get_staticdata()
|
||||
return minetest.serialize(self._staticdata or {})
|
||||
end
|
||||
function DEFAULT_CART_DEF:on_step(dtime)
|
||||
local staticdata = self._staticdata
|
||||
if not staticdata then
|
||||
|
@ -765,11 +868,14 @@ function DEFAULT_CART_DEF:on_step(dtime)
|
|||
local r = 0.6
|
||||
for _, node_pos in pairs({{r, 0}, {0, r}, {-r, 0}, {0, -r}}) do
|
||||
if minetest.get_node(vector.offset(pos, node_pos[1], 0, node_pos[2])).name == "mcl_core:cactus" then
|
||||
self:on_death()
|
||||
--[[
|
||||
detach_driver(self)
|
||||
local drop = self.drop
|
||||
for d = 1, #drop do
|
||||
minetest.add_item(pos, drop[d])
|
||||
end
|
||||
]]
|
||||
self.object:remove()
|
||||
return
|
||||
end
|
||||
|
@ -777,6 +883,8 @@ function DEFAULT_CART_DEF:on_step(dtime)
|
|||
end
|
||||
function DEFAULT_CART_DEF:on_death(killer)
|
||||
local staticdata = self._staticdata
|
||||
minetest.log("action", "cart #"..staticdata.uuid.." was killed")
|
||||
|
||||
detach_driver(self)
|
||||
|
||||
-- Detach passenger
|
||||
|
@ -792,6 +900,9 @@ function DEFAULT_CART_DEF:on_death(killer)
|
|||
mcl_log("TODO: handle detatched minecart death")
|
||||
end
|
||||
|
||||
-- Remove data
|
||||
destroy_cart_data(staticdata.uuid)
|
||||
|
||||
-- Drop items
|
||||
local drop = self.drop
|
||||
if not killer or not minetest.is_creative_enabled(killer:get_player_name()) then
|
||||
|
@ -838,7 +949,12 @@ function mcl_minecarts.place_minecart(itemstack, pointed_thing, placer)
|
|||
-- Update static data
|
||||
local le = cart:get_luaentity()
|
||||
if le then
|
||||
le._staticdata = make_staticdata( railtype, railpos, cart_dir )
|
||||
local uuid = mcl_util.get_uuid(cart)
|
||||
data = make_staticdata( railtype, railpos, cart_dir )
|
||||
data.uuid = uuid
|
||||
cart_data[uuid] = data
|
||||
le._staticdata = data
|
||||
save_cart_data(le._staticdata.uuid)
|
||||
end
|
||||
|
||||
-- Call placer
|
||||
|
@ -1386,3 +1502,8 @@ if minetest.get_modpath("mcl_wip") then
|
|||
mcl_wip.register_wip_item("mcl_minecarts:furnace_minecart")
|
||||
mcl_wip.register_wip_item("mcl_minecarts:command_block_minecart")
|
||||
end
|
||||
|
||||
minetest.register_globalstep(function(dtime)
|
||||
-- TODO: handle periodically updating out-of-range carts
|
||||
end)
|
||||
|
||||
|
|
|
@ -177,7 +177,7 @@ table_merge(SLOPED_RAIL_DEF,{
|
|||
},
|
||||
})
|
||||
|
||||
local function register_rail_v2(itemstring, ndef)
|
||||
function mod.register_rail(itemstring, ndef)
|
||||
assert(ndef.tiles)
|
||||
|
||||
-- Extract out the craft recipe
|
||||
|
@ -188,15 +188,14 @@ local function register_rail_v2(itemstring, ndef)
|
|||
if not ndef.inventory_image then ndef.inventory_image = ndef.tiles[1] end
|
||||
if not ndef.wield_image then ndef.wield_image = ndef.tiles[1] end
|
||||
|
||||
--print("registering rail "..itemstring.." with definition: "..dump(ndef))
|
||||
print("registering rail "..itemstring.." with definition: "..dump(ndef))
|
||||
|
||||
-- Make registrations
|
||||
minetest.register_node(itemstring, ndef)
|
||||
if craft then minetest.register_craft(craft) end
|
||||
end
|
||||
mod.register_rail = register_rail_v2
|
||||
|
||||
local function register_straight_rail(base_name, tiles, def)
|
||||
function mod.register_straight_rail(base_name, tiles, def)
|
||||
def = def or {}
|
||||
local base_def = table.copy(BASE_DEF)
|
||||
local sloped_def = table.copy(SLOPED_RAIL_DEF)
|
||||
|
@ -235,9 +234,8 @@ local function register_straight_rail(base_name, tiles, def)
|
|||
},
|
||||
}))
|
||||
end
|
||||
mod.register_straight_rail = register_straight_rail
|
||||
|
||||
local function register_curves_rail(base_name, tiles, def)
|
||||
function mod.register_curves_rail(base_name, tiles, def)
|
||||
def = def or {}
|
||||
local base_def = table.copy(BASE_DEF)
|
||||
local sloped_def = table.copy(SLOPED_RAIL_DEF)
|
||||
|
@ -333,9 +331,8 @@ local function register_curves_rail(base_name, tiles, def)
|
|||
},
|
||||
}))
|
||||
end
|
||||
mod.register_curves_rail = register_curves_rail
|
||||
|
||||
local function register_rail_sloped(itemstring, def)
|
||||
function mod.register_rail_sloped(itemstring, def)
|
||||
assert(def.tiles)
|
||||
|
||||
-- Build the node definition
|
||||
|
@ -351,7 +348,6 @@ local function register_rail_sloped(itemstring, def)
|
|||
-- Make registrations
|
||||
minetest.register_node(itemstring, ndef)
|
||||
end
|
||||
mod.register_rail_sloped = register_rail_sloped
|
||||
|
||||
-- Redstone rules
|
||||
local rail_rules_long =
|
||||
|
|
Loading…
Reference in New Issue