[Mod] Carts [carts]

Morn76
Member
Posts: 659
Joined: Sun Feb 16, 2014 19:45

by Morn76 » Post

spillz wrote: I don't really play MC, but my kids do. So maybe obvious if you are a MC player. But why not have mese as part of powered rail recipe? About as realistic as powering it with a mese torch!
I don't play MC either, but I watch Stampy's YT videos. I always build in creative mode, but as far as I see mese *is* part of the crafting recipe for power rails already, isn't it?

The easiest solution is probably to turn mesecons off, then you don't need mese torches for the power rails to work.
spillz wrote: Probably just need to change a couple of parameters for power and friction. Would also like to be able to hit a stationary cart while inside it to make it move (not a lot but enough to get it moving if it has stopped)
The carts mod is a very clever hack so it's not as simple to modify and improve as many other mods. Even understanding how it all works is a challenge. Another feature I miss is that the player should turn as the cart goes around corners.

I think many mods by PilzAdam, as great as they are, seem to be forever stuck in the proof-of-concept stage: The carts mod proves that carts are possible, just as the nether mod proves the Nether can be implemented. But somehow these things never get honed to a point of full everyday usability for normal users. It's wonderful to have a nether so there is glowstone in creative mode, but as a practical source of glowstone in survival the Nether is far too small. And so it is with carts, they are a very nice tech demo but not very useful for maps yet. The person who understands the code doesn't seem to be interested in further development to get really mature mods.

Sokomine
Member
Posts: 4317
Joined: Sun Sep 09, 2012 17:31
GitHub: Sokomine
IRC: Sokomine
In-game: Sokomine

by Sokomine » Post

Morn76 wrote: The person who understands the code doesn't seem to be interested in further development to get really mature mods.
Hm, I think PilzAdam (like many other core devs) is already quite busy with developing Minetest and does not have enough time to really play on servers. There are also technical limitations which the carts cannot easily circumvent.
A list of my mods can be found here.

spillz
Member
Posts: 138
Joined: Thu Feb 13, 2014 05:11

by spillz » Post

Here's a quickie patch that lets the driver of a slowly moving (or stationary) cart punch it to accelerate it. Gotta love open source

Code: Select all

diff --git a/init.lua b/init.lua
index 03bc1a3..c299495 100644
--- a/init.lua
+++ b/init.lua
@@ -75,11 +75,24 @@ function cart:on_punch(puncher, time_from_last_punch, tool_capabilities, directi
         return
     end
 
-    if puncher == self.driver then
+    if puncher == self.driver and (math.abs(self.velocity.x)>1 or math.abs(self.velocity.z)>1) then
         return
     end
 
     local d = cart_func:velocity_to_dir(direction)
+    if d.x==0 and d.z==0 then
+        local fd = minetest.dir_to_facedir(puncher:get_look_dir())
+        if fd == 0 then
+            d.x = 1
+        elseif fd == 1 then
+            d.z = -1
+        elseif fd == 2 then
+            d.x = -1
+        elseif fd == 3 then
+            d.z = 1
+        end
+    end
+
     local s = self.velocity
     if time_from_last_punch > tool_capabilities.full_punch_interval then
         time_from_last_punch = tool_capabilities.full_punch_interval
Making the carts go a bit faster when they hit powered rail and weakening the friction should be trivial too. (But may have performance issues when the cart moves outside of the loaded area)

EDIT: Updated (typo: velocity.y should have been velocity.z)
Attachments

[The extension patch has been deactivated and can no longer be displayed.]

[The extension patch has been deactivated and can no longer be displayed.]

Last edited by spillz on Mon Mar 17, 2014 19:44, edited 1 time in total.

Morn76
Member
Posts: 659
Joined: Sun Feb 16, 2014 19:45

by Morn76 » Post

Sokomine wrote:
Morn76 wrote: The person who understands the code doesn't seem to be interested in further development to get really mature mods.
Hm, I think PilzAdam (like many other core devs) is already quite busy with developing Minetest and does not have enough time to really play on servers. There are also technical limitations which the carts cannot easily circumvent.
Maybe that's the problem then, too many devs wanting to work on the Minetest engine and not enough investing a little time to bring existing mods to a more mature state. Perhaps MT needs an Adopt-a-Mod program. Or a competition, e.g. who can create the best Nether (bigger, with mobs) from Adam's existing nether mod.

spillz
Member
Posts: 138
Joined: Thu Feb 13, 2014 05:11

by spillz » Post

Sokomine wrote:
Morn76 wrote: The person who understands the code doesn't seem to be interested in further development to get really mature mods.
Hm, I think PilzAdam (like many other core devs) is already quite busy with developing Minetest and does not have enough time to really play on servers. There are also technical limitations which the carts cannot easily circumvent.
My 2 cents, many mods need to moved into an official minetest game. Having mods scattered all over github is f******g useless when many of them are essential to a good game. Having more mods in the official repo will put more pressure on people to improve their quality including making the engine changes that are necessary to make them work better. Switch them off by default until they reach an acceptable quality level.

Morn76
Member
Posts: 659
Joined: Sun Feb 16, 2014 19:45

by Morn76 » Post

Spillz, I think the Minetest devs deliberately keep the default game very minimalistic (e.g. no mobs). This is why the Freeminer fork happened which seems to aim for a more full-featured default game like it was in Minetest-classic/-c55.

But no matter whether a mod like carts or nether is or isn't part of the default game, it's pretty clear that many players want mods like these, so they need to be improved anyway.

Daven
Member
Posts: 26
Joined: Mon Jan 20, 2014 21:51
Location: Inside the white whale

by Daven » Post

doesnt work for me

spillz
Member
Posts: 138
Joined: Thu Feb 13, 2014 05:11

by spillz » Post

Daven wrote:doesnt work for me
What doesn't work?

spillz
Member
Posts: 138
Joined: Thu Feb 13, 2014 05:11

by spillz » Post

Morn76 wrote:Spillz, I think the Minetest devs deliberately keep the default game very minimalistic (e.g. no mobs). This is why the Freeminer fork happened which seems to aim for a more full-featured default game like it was in Minetest-classic/-c55.

But no matter whether a mod like carts or nether is or isn't part of the default game, it's pretty clear that many players want mods like these, so they need to be improved anyway.
It doesn't matter if it's in the default game, but more stuff needs to be in the official minetest repos. As it stands, key mods aren't getting the scrutiny and patches they would get if they were part of the official repo. Yet anyone playing the various servers is playing them anyway.

IMHO, this mod and a very stripped down mobs/mobf (with a handful of nicely drawn, nicely animated and feature complete mobs) are essential. Arguably so is mesecons. All the whining about lua being too slow is a total distraction - just like with any other scriptable platform, you gradually add stuff to the c/c++ api to work around the actual performance bottlenecks instead of the hypothetical ones.

Morn76
Member
Posts: 659
Joined: Sun Feb 16, 2014 19:45

by Morn76 » Post

spillz wrote: Arguably so is mesecons. All the whining about lua being too slow is a total distraction - just like with any other scriptable platform, you gradually add stuff to the c/c++ api to work around the actual performance bottlenecks instead of the hypothetical ones.
Plus with LuaJIT and LevelDB, performance is considerably better than that of stock 0.4.9. Less lag and stuttering in singleplayer probably means better performance on servers too. I can't complain about bad performance since I've upgraded from 0.4.9 to git builds.

User avatar
Kilarin
Member
Posts: 896
Joined: Mon Mar 10, 2014 00:36
GitHub: Kilarin

by Kilarin » Post

Spillz wrote:Here's a quickie patch that lets the driver of a slowly moving (or stationary) cart punch it to accelerate it. Gotta love open source
Cool, thank you very much! Works great!

spillz
Member
Posts: 138
Joined: Thu Feb 13, 2014 05:11

by spillz » Post

Kilarin wrote:
Spillz wrote:Here's a quickie patch that lets the driver of a slowly moving (or stationary) cart punch it to accelerate it. Gotta love open source
Cool, thank you very much! Works great!
No worries. I just posted a fix to the previous post (small but important typo)

User avatar
Kilarin
Member
Posts: 896
Joined: Mon Mar 10, 2014 00:36
GitHub: Kilarin

by Kilarin » Post

I'm running minetest 0.4.9 on a linux system. carts is the only mod I've got installed.

So, I built a REALLY long railway to go from my castle to some interesting scenery where I wanted to do some more building. Gravity only for this rail, about 350 blocks long, drops one block down for every 5 forward. The track has only one turn along the entire length.

And it works great. EXCEPT, the mine cart kept STOPPING. Usually fairly near the end of the track, but not on the same node every time, but most often within about the same 20 nodes or so? Although it will occasionally stop just anywhere along the whole length. The cart doesn't slow down and then stop, I'm zipping along at near max speed and it just suddenly QUITS.

So I spent a bunch of time today trying to understand the cart mod, and sticking a LOT of print statements into the init.lua to try and trace what was setting my velocity to zero. And here is where I finally ended up. The velocity is being set to zero in on_step in the
-- Recalcualte the rails that are passed since the last server step
section.

I hit elseif old_dir.z ~= 0 then (because of the direction this section of track is going)
where it calls self.old_velocity, self.old_pos = self:calc_rail_direction(self.old_pos, self.old_velocity)
in calc_rail_direction we call dir = self:get_rail_direction(cart_func.v3:round(p), dir)
in get_rail_direction we first check to see if the rail is in front (as it should be) using if cart_func:is_rail(p) then
is_rail uses return minetest.get_item_group(nn, "rail") ~= 0 to determine if the node ahead is a rail.

But here is where it gets strange. This is running along, working perfectly, detecting that yes, the node in front is a rail, and then all of a sudden I hit a node that returns "ignore" for its name. Which means it's checking empty air, if I've understood your wiki correctly.

So for the rest of the track is_rail is returning values that look like this in my enhanced debug output:
*!* is_rail p x= 842 y= 32 z= -528 nn= default:rail group rail= 1
But then I hit a rail that instead of returning the correct value, it returns ignore:
*!* is_rail p x= 842 y= 32 z= -529 nn= ignore group rail= 0

Here is an image of where I stopped, I'm on node 842,32,528, and you can see that the next node is a rail:
Image
(that clump of dirt stuck on the side of the rail ahead marks the rail it couldn't detect on my previous test run)

Below is a screenshot taken after I removed the cart so you can see better.
Image

node 842,32,-529 is a rail. but is_rail is returning ignore as if it were blank air. (THIS run, the previous run it went past this rail without any problems at all and stopped a few ahead)

Below are relevant excerpts from my debug.txt output (these are all my added print statements)

Code: Select all

*!* grd* top pos x=    842     y=    32     z=    -527
*!* grd*     dir x=    0     y=    0     z=    -1
*!* grd*     d   x=    0     y=    0     z=    -1
*!* grd*     front d.y set 0  x=    0     y=    0     z=    -1
*!* grd* addf p x=    842     y=    32     z=    -528
*!* is_rail p x=    842     y=    32     z=    -528     nn=    default:rail     group rail=    1
*!* grd* israil front
*!* crd after grd dir x=    0     y=    0     z=    -1

(above works fine, obviously, but then on the next node we get:)

*!* grd* top pos x=    842     y=    32     z=    -528
*!* grd*     dir x=    0     y=    0     z=    -1
*!* grd*     d   x=    0     y=    0     z=    -1
*!* grd*     front d.y set 0  x=    0     y=    0     z=    -1
*!* grd* addf p x=    842     y=    32     z=    -529
*!* is_rail p x=    842     y=    32     z=    -529     nn=    ignore     group rail=    0
*!* grd*     down d.y set -1  x=    0     y=    -1     z=    -1
*!* grd* addd p x=    842     y=    31     z=    -529
*!* is_rail p x=    842     y=    31     z=    -529     nn=    default:cobble     group rail=    0
*!* grd*     up d.y set 1  x=    0     y=    1     z=    -1
*!* grd* addu p x=    842     y=    33     z=    -529
*!* is_rail p x=    842     y=    33     z=    -529     nn=    ignore     group rail=    0
*!* grd* checking l and r
*!* is_rail p x=    843     y=    32     z=    -528     nn=    air     group rail=    0
*!* is_rail p x=    843     y=    31     z=    -528     nn=    air     group rail=    0
*!* is_rail p x=    841     y=    32     z=    -528     nn=    air     group rail=    0
*!* is_rail p x=    841     y=    31     z=    -528     nn=    air     group rail=    0
*!* grd* end return 0,0,0
*!* crd after grd dir x=    0     y=    0     z=    0
here is my init.lua with the added print statements:

Code: Select all

dofile(minetest.get_modpath("carts").."/functions.lua")

--
-- Cart entity
--

local cart = {
    physical = false,
    collisionbox = {-0.5,-0.5,-0.5, 0.5,0.5,0.5},
    visual = "mesh",
    mesh = "cart.x",
    visual_size = {x=1, y=1},
    textures = {"cart.png"},

    driver = nil,
    velocity = {x=0, y=0, z=0},
    old_pos = nil,
    old_velocity = nil,
    pre_stop_dir = nil,
    MAX_V = 8, -- Limit of the velocity -- *!* changed from 8 to 10
}

function cart:on_rightclick(clicker)
    if not clicker or not clicker:is_player() then
        return
    end
    if self.driver and clicker == self.driver then
        self.driver = nil
        clicker:set_detach()
    elseif not self.driver then
        self.driver = clicker
        clicker:set_attach(self.object, "", {x=0,y=5,z=0}, {x=0,y=0,z=0})
    end
end

function cart:on_activate(staticdata, dtime_s)
    self.object:set_armor_groups({immortal=1})
    if staticdata then
        local tmp = minetest.deserialize(staticdata)
        if tmp then
            self.velocity = tmp.velocity
        end
        if tmp and tmp.pre_stop_dir then
            self.pre_stop_dir = tmp.pre_stop_dir
        end
    end
    self.old_pos = self.object:getpos()
    self.old_velocity = self.velocity
end

function cart:get_staticdata()
    return minetest.serialize({
        velocity = self.velocity,
        pre_stop_dir = self.pre_stop_dir,
    })
end

-- Remove the cart if holding a tool or accelerate it
function cart:on_punch(puncher, time_from_last_punch, tool_capabilities, direction)
    if not puncher or not puncher:is_player() then
        return
    end

    if puncher:get_player_control().sneak then
        self.object:remove()
        local inv = puncher:get_inventory()
        if minetest.setting_getbool("creative_mode") then
            if not inv:contains_item("main", "carts:cart") then
                inv:add_item("main", "carts:cart")
            end
        else
            inv:add_item("main", "carts:cart")
        end
        return
    end

--    if puncher == self.driver then
  if puncher == self.driver and (math.abs(self.velocity.x)>1 or math.abs(self.velocity.z)>1) then
        return
    end

    local d = cart_func:velocity_to_dir(direction)
     if d.x==0 and d.z==0 then
        local fd = minetest.dir_to_facedir(puncher:get_look_dir())
        if fd == 0 then
            d.x = 1
        elseif fd == 1 then
            d.z = -1
        elseif fd == 2 then
            d.x = -1
        elseif fd == 3 then
            d.z = 1
        end
    end


    local s = self.velocity
    if time_from_last_punch > tool_capabilities.full_punch_interval then
        time_from_last_punch = tool_capabilities.full_punch_interval
    end
    local f = 4*(time_from_last_punch/tool_capabilities.full_punch_interval)
    local v = {x=s.x+d.x*f, y=s.y, z=s.z+d.z*f}
    if math.abs(v.x) < 6 and math.abs(v.z) < 6 then
        self.velocity = v
    else
        if math.abs(self.velocity.x) < 6 and math.abs(v.x) >= 6 then
            self.velocity.x = 6*cart_func:get_sign(self.velocity.x)
        end
        if math.abs(self.velocity.z) < 6 and math.abs(v.z) >= 6 then
            self.velocity.z = 6*cart_func:get_sign(self.velocity.z)
        end
    end
end

-- Returns the direction as a unit vector
function cart:get_rail_direction(pos, dir)
    local d = cart_func.v3:copy(dir)
  print("*!* grd* top pos x=",pos.x," y=",pos.y," z=",pos.z)
  print("*!* grd*     dir x=",dir.x," y=",dir.y," z=",dir.z)
  print("*!* grd*     d   x=",d.x," y=",d.y," z=",d.z)    

    -- Check front
    d.y = 0
  print("*!* grd*     front d.y set 0  x=",d.x," y=",d.y," z=",d.z)   
    local p = cart_func.v3:add(cart_func.v3:copy(pos), d)
  print("*!* grd* addf p x=",p.x," y=",p.y," z=",p.z)  
    if cart_func:is_rail(p) then
    print("*!* grd* israil front")
        return d
    end

    -- Check downhill
    d.y = -1
  print("*!* grd*     down d.y set -1  x=",d.x," y=",d.y," z=",d.z)     
    p = cart_func.v3:add(cart_func.v3:copy(pos), d)
  print("*!* grd* addd p x=",p.x," y=",p.y," z=",p.z)    
    if cart_func:is_rail(p) then
    print("*!* grd* israil down")  
        return d
    end

    -- Check uphill
    d.y = 1
  print("*!* grd*     up d.y set 1  x=",d.x," y=",d.y," z=",d.z)       
    p = cart_func.v3:add(cart_func.v3:copy(pos), d)
  print("*!* grd* addu p x=",p.x," y=",p.y," z=",p.z)    
    if cart_func:is_rail(p) then
    print("*!* grd* israil up")    
        return d
    end
    d.y = 0

    -- Check left and right
    local view_dir
    local other_dir
    local a

  print("*!* grd* checking l and r ")  
    if d.x == 0 and d.z ~= 0 then
        view_dir = "z"
        other_dir = "x"
        if d.z < 0 then
            a = {1, -1}
        else
            a = {-1, 1}
        end
    elseif d.z == 0 and d.x ~= 0 then
        view_dir = "x"
        other_dir = "z"
        if d.x > 0 then
            a = {1, -1}
        else
            a = {-1, 1}
        end
    else
        return {x=0, y=0, z=0}
    end

    d[view_dir] = 0
    d[other_dir] = a[1]
    p = cart_func.v3:add(cart_func.v3:copy(pos), d)
    if cart_func:is_rail(p) then
        return d
    end
    d.y = -1
    p = cart_func.v3:add(cart_func.v3:copy(pos), d)
    if cart_func:is_rail(p) then
        return d
    end
    d.y = 0
    d[other_dir] = a[2]
    p = cart_func.v3:add(cart_func.v3:copy(pos), d)
    if cart_func:is_rail(p) then
        return d
    end
    d.y = -1
    p = cart_func.v3:add(cart_func.v3:copy(pos), d)
    if cart_func:is_rail(p) then
        return d
    end
    d.y = 0

  print("*!* grd* end return 0,0,0") --*!* debug
    return {x=0, y=0, z=0}

end

function cart:calc_rail_direction(pos, vel)
  print("*!* crd top vel       x=",vel.x," y=",vel.y," z=",vel.z)
    local velocity = cart_func.v3:copy(vel)
  print("*!* crd copy velocity x=",velocity.x," y=",velocity.y," z=",velocity.z)
    local p = cart_func.v3:copy(pos)
    if cart_func:is_int(p.x) and cart_func:is_int(p.z) then
    print("*!* crd inside if")
        local dir = cart_func:velocity_to_dir(velocity)
    print("*!* crd after vtd velocity x=",velocity.x," y=",velocity.y," z=",velocity.z)
        local dir_old = cart_func.v3:copy(dir)

        dir = self:get_rail_direction(cart_func.v3:round(p), dir)
    print("*!* crd after grd dir x=",dir.x," y=",dir.y," z=",dir.z)
        local v = math.max(math.abs(velocity.x), math.abs(velocity.z))
    print("*!* crd after math.max v=",v)
    
        velocity = {
            x = v * dir.x,
            y = v * dir.y,
            z = v * dir.z,
        }

        if cart_func.v3:equal(velocity, {x=0, y=0, z=0}) then
      print("*!* calc_rail_direction vel=0,0,0") --*!* debug
            -- First try this HACK
            -- Move the cart on the rail if above or under it
            if cart_func:is_rail(cart_func.v3:add(p, {x=0, y=1, z=0})) and vel.y >= 0 then
                p = cart_func.v3:add(p, {x=0, y=1, z=0})
                return self:calc_rail_direction(p, vel)
            end
            if cart_func:is_rail(cart_func.v3:add(p, {x=0, y=-1, z=0})) and vel.y <= 0  then
                p = cart_func.v3:add(p, {x=0, y=-1, z=0})
                return self:calc_rail_direction(p, vel)
            end
            -- Now the HACK gets really dirty
            if cart_func:is_rail(cart_func.v3:add(p, {x=0, y=2, z=0})) and vel.y >= 0 then
                p = cart_func.v3:add(p, {x=0, y=1, z=0})
                return self:calc_rail_direction(p, vel)
            end
            if cart_func:is_rail(cart_func.v3:add(p, {x=0, y=-2, z=0})) and vel.y <= 0 then
                p = cart_func.v3:add(p, {x=0, y=-1, z=0})
                return self:calc_rail_direction(p, vel)
            end

            return {x=0, y=0, z=0}, p
        end

        if not cart_func.v3:equal(dir, dir_old) then
      print("*!* calc_rail_direction not equal dir,dir_old : v.x=",velocity.x," v.y=",velocity.y," v.z=",velocity.z) --*!* debug
            return velocity, cart_func.v3:round(p)
        end

    end
  print("*!* calc rail direction final return v.x=",velocity.x," v.y=",velocity.y," v.z=",velocity.z) --*!* debug
    return velocity, p
end

function cart:on_step(dtime)

    local pos = self.object:getpos()
    local dir = cart_func:velocity_to_dir(self.velocity)
  print("*!* on_step top dir.x=",dir.x," dir.y=",dir.y," dir.z=",dir.z," v.x=",self.velocity.x," v.y=",self.velocity.y," v.z=",self.velocity.z) --*!* debug

    if not cart_func.v3:equal(self.velocity, {x=0,y=0,z=0}) then
        self.pre_stop_dir = cart_func:velocity_to_dir(self.velocity)
    print("*!*   on_step after pre_stop v.x=",self.velocity.x," v.y=",self.velocity.y," v.z=",self.velocity.z) --*!* debug
    end

    -- Stop the cart if the velocity is nearly 0
    -- Only if on a flat railway
  print("*!*   on_step beofore if dir.y==0 v.x=",self.velocity.x," v.y=",self.velocity.y," v.z=",self.velocity.z) --*!* debug
    if dir.y == 0 then
    print("*!*   on_step flat top v.x=",self.velocity.x," v.y=",self.velocity.y," v.z=",self.velocity.z) --*!* debug
        if math.abs(self.velocity.x) < 0.1 and  math.abs(self.velocity.z) < 0.1 then
            -- Start the cart if powered from mesecons
            local a = tonumber(minetest.env:get_meta(pos):get_string("cart_acceleration"))
            if a and a ~= 0 then
                if self.pre_stop_dir and cart_func.v3:equal(self:get_rail_direction(self.object:getpos(), self.pre_stop_dir), self.pre_stop_dir) then
                    self.velocity = {
                        x = self.pre_stop_dir.x * 0.2,
                        y = self.pre_stop_dir.y * 0.2,
                        z = self.pre_stop_dir.z * 0.2,
                    }
                    self.old_velocity = self.velocity
                    return
                end
                for _,y in ipairs({0,-1,1}) do
                    for _,z in ipairs({1,-1}) do
                        if cart_func.v3:equal(self:get_rail_direction(self.object:getpos(), {x=0, y=y, z=z}), {x=0, y=y, z=z}) then
                            self.velocity = {
                                x = 0,
                                y = 0.2*y,
                                z = 0.2*z,
                            }
                            self.old_velocity = self.velocity
                            return
                        end
                    end
                    for _,x in ipairs({1,-1}) do
                        if cart_func.v3:equal(self:get_rail_direction(self.object:getpos(), {x=x, y=y, z=0}), {x=x, y=y, z=0}) then
                            self.velocity = {
                                x = 0.2*x,
                                y = 0.2*y,
                                z = 0,
                            }
                            self.old_velocity = self.velocity
                            return
                        end
                    end
                end
            end

      print("*!*   on_step flat set velocity to zero") --*!* debug
            self.velocity = {x=0, y=0, z=0}
            self.object:setvelocity(self.velocity)
            self.old_velocity = self.velocity
            self.old_pos = self.object:getpos()
            return
        end
  print("*!*   on_step flat bot v.x=",self.velocity.x," v.y=",self.velocity.y," v.z=",self.velocity.z) --*!* debug
    end

    --
    -- Set the new moving direction
    --

    -- Recalcualte the rails that are passed since the last server step
    local old_dir = cart_func:velocity_to_dir(self.old_velocity)
  print("*!*   on_step recalc olddir.x=",old_dir.x," olddir.y=",old_dir.y," olddir.z=",old_dir.z," v.x=",self.velocity.x," v.y=",self.velocity.y," v.z=",self.velocity.z) --*!* debug
  print("*!*   on_step recalc    dir.x=",dir.x,"    dir.y=",dir.y,"    dir.z=",dir.z) --*!* debug
  print("*!*   on_step recalc oldvel.x=",self.old_velocity.x," oldvel.y=",self.old_velocity.y," oldvel.z=",self.old_velocity.z) --*!* debug
  print("*!*   on_step recalc oldpos.x=",self.old_pos.x," oldpos.y=",self.old_pos.y," oldpos.z=",self.old_pos.z) --*!* debug
  print("*!*   on_step recalc    pos.x=",pos.x,"    pos.y=",pos.y,"    pos.z=",pos.z) --*!* debug



    if old_dir.x ~= 0 then
        local sign = cart_func:get_sign(pos.x-self.old_pos.x)
    print("*!*   on_step recalc in old_dir.x ~= 0  sign=",sign) --*!* debug
        while true do
            if sign ~= cart_func:get_sign(pos.x-self.old_pos.x) or pos.x == self.old_pos.x then
                break
            end
            self.old_pos.x = self.old_pos.x + cart_func:get_sign(pos.x-self.old_pos.x)*0.1
            self.old_pos.y = self.old_pos.y + cart_func:get_sign(pos.x-self.old_pos.x)*0.1*old_dir.y
            self.old_velocity, self.old_pos = self:calc_rail_direction(self.old_pos, self.old_velocity)
            old_dir = cart_func:velocity_to_dir(self.old_velocity)
            if not cart_func.v3:equal(cart_func:velocity_to_dir(self.old_velocity), dir) then
        print("*!*   on_step recalc if1top v.x=",self.velocity.x," v.y=",self.velocity.y," v.z=",self.velocity.z) --*!* debug
                self.velocity = self.old_velocity
                pos = self.old_pos
                self.object:setpos(self.old_pos)
        print("*!*   on_step recalc if1bot v.x=",self.velocity.x," v.y=",self.velocity.y," v.z=",self.velocity.z) --*!* debug
                break
            end
        end
    elseif old_dir.z ~= 0 then
        local sign = cart_func:get_sign(pos.z-self.old_pos.z)
    print("*!*   on_step recalc in old_dir.z ~= 0  sign=",sign) --*!* debug
        while true do
      print("*!*   on_step recalc whiletrue top sign=",sign," oldpos.x=",self.old_pos.x," oldpos.y=",self.old_pos.y," oldpos.z=",self.old_pos.z) --*!* debug
      print("*!*   on_step recalc                            pos.x=",pos.x,"    pos.y=",pos.y,"    pos.z=",pos.z) --*!* debug
      print("*!*   on_step recalc                         oldvel.x=",self.old_velocity.x," oldvel.y=",self.old_velocity.y," oldvel.z=",self.old_velocity.z) --*!* debug
            if sign ~= cart_func:get_sign(pos.z-self.old_pos.z) or pos.z == self.old_pos.z then
        print("*!*   on_step recalc break") --*!* debug
                break
            end
            self.old_pos.z = self.old_pos.z + cart_func:get_sign(pos.z-self.old_pos.z)*0.1
            self.old_pos.y = self.old_pos.y + cart_func:get_sign(pos.z-self.old_pos.z)*0.1*old_dir.y
      print("*!*   on_step recalc    bfr crd              oldpos.x=",self.old_pos.x," oldpos.y=",self.old_pos.y," oldpos.z=",self.old_pos.z) --*!* debug
      print("*!*   on_step recalc    bfr crd              oldvel.x=",self.old_velocity.x," oldvel.y=",self.old_velocity.y," oldvel.z=",self.old_velocity.z) --*!* debug
            self.old_velocity, self.old_pos = self:calc_rail_direction(self.old_pos, self.old_velocity)
      print("*!*   on_step recalc    aft crd              oldpos.x=",self.old_pos.x," oldpos.y=",self.old_pos.y," oldpos.z=",self.old_pos.z) --*!* debug
      print("*!*   on_step recalc    aft crd              oldvel.x=",self.old_velocity.x," oldvel.y=",self.old_velocity.y," oldvel.z=",self.old_velocity.z) --*!* debug
            old_dir = cart_func:velocity_to_dir(self.old_velocity)
      print("*!*   on_step recalc    dropped              oldvel.x=",self.old_velocity.x," oldvel.y=",self.old_velocity.y," oldvel.z=",self.old_velocity.z) --*!* debug
      print("*!*   on_step recalc    dropped              oldpos.x=",self.old_pos.x," oldpos.y=",self.old_pos.y," oldpos.z=",self.old_pos.z) --*!* debug
      print("*!*   on_step recalc    dropped              olddir.x=",old_dir.x," olddir.y=",old_dir.y," olddir.z=",old_dir.z) --*!* debug
            if not cart_func.v3:equal(cart_func:velocity_to_dir(self.old_velocity), dir) then
        print("*!*   on_step recalc if2top v.x=",self.velocity.x," v.y=",self.velocity.y," v.z=",self.velocity.z) --*!* debug
                self.velocity = self.old_velocity
        print("*!*   on_step recalc if2bot v.x=",self.velocity.x," v.y=",self.velocity.y," v.z=",self.velocity.z) --*!* debug
                pos = self.old_pos
                self.object:setpos(self.old_pos)
                break
            end
    print("*!*   on_step recalc whiletrue bot sign=",sign," oldpos.x=",self.old_pos.x," oldpos.y=",self.old_pos.y," oldpos.z=",self.old_pos.z) --*!* debug
    print("*!*   on_step recalc                            pos.x=",pos.x,"    pos.y=",pos.y,"    pos.z=",pos.z) --*!* debug
    print("*!*   on_step recalc                         oldvel.x=",self.old_velocity.x," oldvel.y=",self.old_velocity.y," oldvel.z=",self.old_velocity.z) --*!* debug
        end
    end

    -- Calculate the new step
    self.velocity, pos = self:calc_rail_direction(pos, self.velocity)
    self.object:setpos(pos)
    dir = cart_func:velocity_to_dir(self.velocity)

    -- Accelerate or decelerate the cart according to the pitch and acceleration of the rail node
    local a = tonumber(minetest.env:get_meta(pos):get_string("cart_acceleration"))
    if not a then
        a = 0
    end
  print("*!*   on_step acel-decl top v.x=",self.velocity.x," v.y=",self.velocity.y," v.z=",self.velocity.z) --*!* debug
    if self.velocity.y < 0 then
        self.velocity = {
            x = self.velocity.x + (a+0.13)*cart_func:get_sign(self.velocity.x),
            y = self.velocity.y + (a+0.13)*cart_func:get_sign(self.velocity.y),
            z = self.velocity.z + (a+0.13)*cart_func:get_sign(self.velocity.z),
        }
    elseif self.velocity.y > 0 then
        self.velocity = {
            x = self.velocity.x + (a-0.1)*cart_func:get_sign(self.velocity.x),
            y = self.velocity.y + (a-0.1)*cart_func:get_sign(self.velocity.y),
            z = self.velocity.z + (a-0.1)*cart_func:get_sign(self.velocity.z),
        }
    else
        self.velocity = {
            x = self.velocity.x + (a-0.03)*cart_func:get_sign(self.velocity.x),
            y = self.velocity.y + (a-0.03)*cart_func:get_sign(self.velocity.y),
            z = self.velocity.z + (a-0.03)*cart_func:get_sign(self.velocity.z),
        }

        -- Place the cart exactly on top of the rail
        if cart_func:is_rail(cart_func.v3:round(pos)) then
            self.object:setpos({x=pos.x, y=math.floor(pos.y+0.5), z=pos.z})
            pos = self.object:getpos()
        end
  print("*!*   on_step acel-decl bot v.x=",self.velocity.x," v.y=",self.velocity.y," v.z=",self.velocity.z) --*!* debug
    end

    -- Dont switch moving direction
    -- Only if on flat railway
    if dir.y == 0 then
    print("*!* on_step   flat-chng dir top v.x=",self.velocity.x," v.y=",self.velocity.y," v.z=",self.velocity.z) --*!* debug
        if cart_func:get_sign(dir.x) ~= cart_func:get_sign(self.velocity.x) then
            self.velocity.x = 0
        end
        if cart_func:get_sign(dir.y) ~= cart_func:get_sign(self.velocity.y) then
            self.velocity.y = 0
        end
        if cart_func:get_sign(dir.z) ~= cart_func:get_sign(self.velocity.z) then
            self.velocity.z = 0
        end
    print("*!* on_step   flat-chng dir bot v.x=",self.velocity.x," v.y=",self.velocity.y," v.z=",self.velocity.z) --*!* debug
    end

    -- Allow only one moving direction (multiply the other one with 0)
    dir = cart_func:velocity_to_dir(self.velocity)
    self.velocity = {
        x = math.abs(self.velocity.x) * dir.x,
        y = self.velocity.y,
        z = math.abs(self.velocity.z) * dir.z,
    }
  print("*!* on_step   aft onedir v.x=",self.velocity.x," v.y=",self.velocity.y," v.z=",self.velocity.z) --*!* debug


    -- Move cart exactly on the rail
    if dir.x ~= 0 and not cart_func:is_int(pos.z) then
        pos.z = math.floor(0.5+pos.z)
        self.object:setpos(pos)
    elseif dir.z ~= 0 and not cart_func:is_int(pos.x) then
        pos.x = math.floor(0.5+pos.x)
        self.object:setpos(pos)
    end

    -- Limit the velocity
    if math.abs(self.velocity.x) > self.MAX_V then
        self.velocity.x = self.MAX_V*cart_func:get_sign(self.velocity.x)
    end
    if math.abs(self.velocity.y) > self.MAX_V then
        self.velocity.y = self.MAX_V*cart_func:get_sign(self.velocity.y)
    end
    if math.abs(self.velocity.z) > self.MAX_V then
        self.velocity.z = self.MAX_V*cart_func:get_sign(self.velocity.z)
    end
  print("*!* on_step   aft limit dir top v.x=",self.velocity.x," v.y=",self.velocity.y," v.z=",self.velocity.z) --*!* debug


    self.object:setvelocity(self.velocity)

    self.old_pos = self.object:getpos()
    self.old_velocity = cart_func.v3:copy(self.velocity)

    if dir.x < 0 then
        self.object:setyaw(math.pi/2)
    elseif dir.x > 0 then
        self.object:setyaw(3*math.pi/2)
    elseif dir.z < 0 then
        self.object:setyaw(math.pi)
    elseif dir.z > 0 then
        self.object:setyaw(0)
    end

    if dir.y == -1 then
        self.object:set_animation({x=1, y=1}, 1, 0)
    elseif dir.y == 1 then
        self.object:set_animation({x=2, y=2}, 1, 0)
    else
        self.object:set_animation({x=0, y=0}, 1, 0)
    end

end

minetest.register_entity("carts:cart", cart)


minetest.register_craftitem("carts:cart", {
    description = "Minecart",
    inventory_image = minetest.inventorycube("cart_top.png", "cart_side.png", "cart_side.png"),
    wield_image = "cart_side.png",

    on_place = function(itemstack, placer, pointed_thing)
        if not pointed_thing.type == "node" then
            return
        end
        if cart_func:is_rail(pointed_thing.under) then
            minetest.env:add_entity(pointed_thing.under, "carts:cart")
            if not minetest.setting_getbool("creative_mode") then
                itemstack:take_item()
            end
            return itemstack
        elseif cart_func:is_rail(pointed_thing.above) then
            minetest.env:add_entity(pointed_thing.above, "carts:cart")
            if not minetest.setting_getbool("creative_mode") then
                itemstack:take_item()
            end
            return itemstack
        end
    end,
})

minetest.register_craft({
    output = "carts:cart",
    recipe = {
        {"", "", ""},
        {"default:steel_ingot", "", "default:steel_ingot"},
        {"default:steel_ingot", "default:steel_ingot", "default:steel_ingot"},
    },
})

--
-- Mesecon support
--

minetest.register_node(":default:rail", {
    description = "Rail",
    drawtype = "raillike",
    tiles = {"default_rail.png", "default_rail_curved.png", "default_rail_t_junction.png", "default_rail_crossing.png"},
    inventory_image = "default_rail.png",
    wield_image = "default_rail.png",
    paramtype = "light",
    is_ground_content = true,
    walkable = false,
    selection_box = {
        type = "fixed",
        -- but how to specify the dimensions for curved and sideways rails?
        fixed = {-1/2, -1/2, -1/2, 1/2, -1/2+1/16, 1/2},
    },
    groups = {bendy=2,snappy=1,dig_immediate=2,attached_node=1,rail=1,connect_to_raillike=1},
})

minetest.register_node("carts:powerrail", {
    description = "Powered Rail",
    drawtype = "raillike",
    tiles = {"carts_rail_pwr.png", "carts_rail_curved_pwr.png", "carts_rail_t_junction_pwr.png", "carts_rail_crossing_pwr.png"},
    inventory_image = "carts_rail_pwr.png",
    wield_image = "carts_rail_pwr.png",
    paramtype = "light",
    is_ground_content = true,
    walkable = false,
    selection_box = {
        type = "fixed",
        -- but how to specify the dimensions for curved and sideways rails?
        fixed = {-1/2, -1/2, -1/2, 1/2, -1/2+1/16, 1/2},
    },
    groups = {bendy=2,snappy=1,dig_immediate=2,attached_node=1,rail=1,connect_to_raillike=1},

    after_place_node = function(pos, placer, itemstack)
        if not mesecon then
            minetest.env:get_meta(pos):set_string("cart_acceleration", "0.5")
        end
    end,

    mesecons = {
        effector = {
            action_on = function(pos, node)
                minetest.env:get_meta(pos):set_string("cart_acceleration", "0.5")
            end,

            action_off = function(pos, node)
                minetest.env:get_meta(pos):set_string("cart_acceleration", "0")
            end,
        },
    },
})

minetest.register_node("carts:brakerail", {
    description = "Brake Rail",
    drawtype = "raillike",
    tiles = {"carts_rail_brk.png", "carts_rail_curved_brk.png", "carts_rail_t_junction_brk.png", "carts_rail_crossing_brk.png"},
    inventory_image = "carts_rail_brk.png",
    wield_image = "carts_rail_brk.png",
    paramtype = "light",
    is_ground_content = true,
    walkable = false,
    selection_box = {
        type = "fixed",
        -- but how to specify the dimensions for curved and sideways rails?
        fixed = {-1/2, -1/2, -1/2, 1/2, -1/2+1/16, 1/2},
    },
    groups = {bendy=2,snappy=1,dig_immediate=2,attached_node=1,rail=1,connect_to_raillike=1},

    after_place_node = function(pos, placer, itemstack)
        if not mesecon then
            minetest.env:get_meta(pos):set_string("cart_acceleration", "-0.2")
        end
    end,

    mesecons = {
        effector = {
            action_on = function(pos, node)
                minetest.env:get_meta(pos):set_string("cart_acceleration", "-0.2")
            end,

            action_off = function(pos, node)
                minetest.env:get_meta(pos):set_string("cart_acceleration", "0")
            end,
        },
    },
})

minetest.register_craft({
    output = "carts:powerrail 2",
    recipe = {
        {"default:steel_ingot", "default:mese_crystal_fragment", "default:steel_ingot"},
        {"default:steel_ingot", "default:stick", "default:steel_ingot"},
        {"default:steel_ingot", "", "default:steel_ingot"},
    }
})

minetest.register_craft({
    output = "carts:powerrail 2",
    recipe = {
        {"default:steel_ingot", "", "default:steel_ingot"},
        {"default:steel_ingot", "default:stick", "default:steel_ingot"},
        {"default:steel_ingot", "default:mese_crystal_fragment", "default:steel_ingot"},
    }
})

minetest.register_craft({
    output = "carts:brakerail 2",
    recipe = {
        {"default:steel_ingot", "default:coal_lump", "default:steel_ingot"},
        {"default:steel_ingot", "default:stick", "default:steel_ingot"},
        {"default:steel_ingot", "", "default:steel_ingot"},
    }
})

minetest.register_craft({
    output = "carts:brakerail 2",
    recipe = {
        {"default:steel_ingot", "", "default:steel_ingot"},
        {"default:steel_ingot", "default:stick", "default:steel_ingot"},
        {"default:steel_ingot", "default:coal_lump", "default:steel_ingot"},
    }
})

And this is functions.lua with my added print statements:

Code: Select all

--
-- Helper functions
--

cart_func = {}

function cart_func:get_sign(z)
    if z == 0 then
        return 0
    else
        return z/math.abs(z)
    end
end

-- Returns the velocity as a unit vector
-- The smaller part of the vector will be turned to 0
function cart_func:velocity_to_dir(v)
    if math.abs(v.x) > math.abs(v.z) then
        return {x=cart_func:get_sign(v.x), y=cart_func:get_sign(v.y), z=0}
    else
        return {x=0, y=cart_func:get_sign(v.y), z=cart_func:get_sign(v.z)}
    end
end

function cart_func:is_rail(p)
    local nn = minetest.env:get_node(p).name
  print("*!* is_rail p x=",p.x," y=",p.y," z=",p.z," nn=",nn," group rail=", minetest.get_item_group(nn, "rail"))
    return minetest.get_item_group(nn, "rail") ~= 0
end

function cart_func:is_int(z)
    z = math.abs(z)
    return math.abs(math.floor(z+0.5)-z) <= 0.1
end

cart_func.v3 = {}

function cart_func.v3:add(v1, v2)
    return {x=v1.x+v2.x, y=v1.y+v2.y, z=v1.z+v2.z}
end

function cart_func.v3:copy(v)
    return {x=v.x, y=v.y, z=v.z}
end

function cart_func.v3:round(v)
    return {
        x = math.floor(v.x+0.5),
        y = math.floor(v.y+0.5),
        z = math.floor(v.z+0.5),
    }
end

function cart_func.v3:equal(v1, v2)
    return v1.x == v2.x and v1.y == v2.y and v1.z == v2.z
end
Since the problem seems to be with a result coming back from minetest.get_item_group, I'm assuming this is NOT a problem with the cart mod? Should I move my question into the bugs forum?

Thanks in advance for any help!
Last edited by Kilarin on Wed Mar 19, 2014 04:45, edited 1 time in total.

Temperest
Member
Posts: 651
Joined: Tue Nov 15, 2011 23:13
GitHub: Uberi

by Temperest » Post

Nice debugging! Unfortunately this is an engine issue and is unrelated to the carts mod itself.

You should open an issue at https://github.com/minetest/minetest/issues/new, where the minetest core devs can take a look at it. This is tangentially related to issues #529 and #572, but was not the direct topic of discussion and should deserve its own issue.
WorldEdit 1.0 released

The Mesecons Laboratory - the art of Mesecons circuitry
Latest article: Mesecons Basics.

User avatar
Kilarin
Member
Posts: 896
Joined: Mon Mar 10, 2014 00:36
GitHub: Kilarin

by Kilarin » Post

Temperest wrote:this is an engine issue and is unrelated to the carts mod itself.
That's what I figured. Will do, thanks!

<edit>
and done:
https://github.com/minetest/minetest/issues/1177
Last edited by Kilarin on Wed Mar 19, 2014 14:17, edited 1 time in total.

User avatar
minermoder27
Member
Posts: 138
Joined: Wed Nov 20, 2013 23:24
GitHub: ZNixian
In-game: minermoder27

by minermoder27 » Post

Maybe try calling the function again if you get node=ignore
Last edited by minermoder27 on Wed Mar 19, 2014 23:13, edited 1 time in total.
My best mods:
Buildtest

User avatar
Kilarin
Member
Posts: 896
Joined: Mon Mar 10, 2014 00:36
GitHub: Kilarin

by Kilarin » Post

minermoder27 wrote:Maybe try calling the function again if you get node=ignore
That is an excellent idea!
BUT, when trying it, I just got ignore the second time as well. :(

User avatar
minermoder27
Member
Posts: 138
Joined: Wed Nov 20, 2013 23:24
GitHub: ZNixian
In-game: minermoder27

by minermoder27 » Post

* sigh *
This would take more work, but what about caching the next x pieces of track?
Last edited by minermoder27 on Thu Mar 20, 2014 01:14, edited 1 time in total.
My best mods:
Buildtest

User avatar
minermoder27
Member
Posts: 138
Joined: Wed Nov 20, 2013 23:24
GitHub: ZNixian
In-game: minermoder27

by minermoder27 » Post

I realized you could use Voxel Manipulator to get a block's name.

If you add this:

Code: Select all

function cart_func:get_content_voxel(pos)
    local t1 = os.clock()
    local vm = minetest.get_voxel_manip()
    local pos1, pos2 = vm:read_from_map(vector.add(pos, {x=-1,y=-1,z=-1}),vector.add(pos, {x=1,y=1,z=1}))
    local a = VoxelArea:new{
        MinEdge=pos1,
        MaxEdge=pos2,
    }
     
    local data = vm:get_data()
    local vi = a:indexp(pos)
    local railid = data[vi]
    local real_name = minetest.get_name_from_content_id(railid)
    print(string.format("voxel-ing rail: elapsed time: %.2fms", (os.clock() - t1) * 1000))
    return real_name
end
to functions.lua, and changed this:

Code: Select all

function cart_func:is_rail(p, is_voxel)
    local nn = minetest.env:get_node(p).name
++  if nn=="ignore" then
++      print("oops")
++      nn=cart_func:get_content_voxel(p)
++  end
    return minetest.get_item_group(nn, "rail") ~= 0
end
code, it seems to work

PS:
It is MUCH slower, but it works
Last edited by minermoder27 on Thu Mar 20, 2014 03:34, edited 1 time in total.
My best mods:
Buildtest

User avatar
celeron55
Administrator
Posts: 544
Joined: Tue Apr 19, 2011 10:10
GitHub: celeron55
IRC: celeron55

by celeron55 » Post

The issue probably is that the engine on the server side fails to keep the MapBlocks around the player loaded properly, probably due to a bug.

The vm:read_from_map solution works because the VoxelManipulator Lua interface will force-load the requested area before returning anything.

There is also a force-loading API, but I took a look at its current state and noticed that it's too crappy to be recommended to anyone. Expect it to change to be better sometime during... eh... this year.

EDIT: It seems like the original problem might be fully fixed by this:

https://github.com/minetest/minetest/co ... 17d44be8fb

Please test with latest git.

User avatar
minermoder27
Member
Posts: 138
Joined: Wed Nov 20, 2013 23:24
GitHub: ZNixian
In-game: minermoder27

by minermoder27 » Post

Made a pull request to fix the bug on non-latest-git builds
My best mods:
Buildtest

User avatar
Kilarin
Member
Posts: 896
Joined: Mon Mar 10, 2014 00:36
GitHub: Kilarin

by Kilarin » Post

minermoder27 wrote:I realized you could use Voxel Manipulator to get a block's name.
That WORKS! Thank you VERY much!
celeron55 wrote:It seems like the original problem might be fully fixed by this:
https://github.com/minetest/minetest/co … 17d44be8fb
Please test with latest git.
That does sound right. But please excuse my ignorance, I'm new to git. How can I find the latest linux binaries with the updated code? Or would I need to pull the c code and do a build?
minermoder27 wrote:Made a pull request to fix the bug on non-latest-git builds
That would make it easier.

Thanks folks! My cart runs all the way through now without stopping!

spillz
Member
Posts: 138
Joined: Thu Feb 13, 2014 05:11

by spillz » Post

@kilarin - there are daily builds for some distros. Check the download page.

User avatar
Kilarin
Member
Posts: 896
Joined: Mon Mar 10, 2014 00:36
GitHub: Kilarin

by Kilarin » Post

Thanks spillz!
I upgraded to the latest build minetestc55_201403250545-0~2670~ubuntu12.04.1_i386
and now I no longer get an ignore, and yet the cart stops anyway. I now seem to be getting values of air and cobble when it stops. Should be impossible to get a value of cobble, since the only stone around is all covered by rail. It looks like the cart might actually be losing track of its place. Since I also got some strange results where the cart stopped below or above the track.
Image
(the above picture resulted in a LONG fall into the water... :)

Image

I have debug.txt output with extensive extra print statements if anyone wants them.
If no one else is experiancing similiar problems, this may be unique to my system. I'm having trouble with just about any mod I try to add. Most of them seem to crash minetest. So far I can use inventory_plus, zcg, and carts. And carts is flaky.
And in vanilla minetest I get an occasional error where my x coord suddenly goes to infinity (well, max long int) and I have to reset it in singleplayer. So my problems may all be related to... me. :)

TheBlueWanderer
New member
Posts: 1
Joined: Fri Mar 28, 2014 03:12

by TheBlueWanderer » Post

Why if I Ride the Cart its Always Separating the Rails ?
Last edited by TheBlueWanderer on Wed Apr 02, 2014 10:38, edited 1 time in total.

Post Reply