Merged priv

master
Gabriel Pérez-Cerezo 2017-01-18 23:22:06 +01:00
commit fc67d4531a
No known key found for this signature in database
GPG Key ID: 90422B01A46D0B3E
283 changed files with 1799 additions and 913 deletions

View File

@ -1,5 +1,4 @@
tarball: clean
# tar czf advtrains.tar.gz textures/*.png models/*.b3d *.lua *.txt
which zip && zip advtrains.zip textures/*.png models/*.b3d *.lua *.txt
which zip && zip -r advtrains.zip advtrains
clean:
rm -f advtrains.zip advtrains.tar.gz
rm -f advtrains.zip

Binary file not shown.

BIN
advtrains/advtrains.zip Normal file

Binary file not shown.

View File

@ -0,0 +1,114 @@
Advanced Trains [advtrains] API documentation
--------
To use the API, mods must depend on 'advtrains'.
All boolean values in definition tables default to 'false' and can be omitted.
### Wagons
Wagons are registered using the function
advtrains.register_wagon(name, prototype, description, inventory_image)
- 'name' is the internal name of the wagon. It is registered inside the 'advtrains:' namespace.
Example: A wagon with name="engine_tgv" will be registered as "advtrains:engine_tgv".
- 'prototype' is the lua entity prototype. The regular definition keys for luaentites apply. Additional required and optional properties see below. DO NOT define 'on_step', 'on_activate', 'on_punch', 'on_rightclick' and 'get_staticdata' since these will be overridden. Use 'custom_*' instead.
- 'description' is the description of the inventory item that is used to place the wagon.
- 'inventory_image' is the inventory image of said item.
# Wagon prototype properties
{
... all regular luaentity properties (mesh, textures, collisionbox a.s.o)...
drives_on = {default=true},
^- used to define the tracktypes (see below) that wagon can drive on. The tracktype identifiers are given as keys, similar to privileges)
max_speed = 10,
^- optional, default 10: defines the maximum speed this wagon can drive. The maximum speed of a train is determined by the wagon with the lowest max_speed value.
seats = {
{
name="Left front window",
^- display name of this seat
attach_offset={x=0, y=10, z=0},
^- this value is passed to 'set_attach'
view_offset={x=0, y=6, z=0},
^- player:set_eye_offset is called with this parameter.
driving_ctrl_access=false,
^- If the seat is a driver stand, and players sitting here should get access to the train's driving control.
},
},
^- contains zero or more seat definitions. A seat is a place where a player can be attached when getting on a wagon.
wagon_span=2,
^- How far this wagon extends from its base position. Is the half of the wagon length.
^- Used to determine in which distance the other wagons have to be positioned. Will require tweaking.
drops = {"default:steelblock 3"}
^- List of itemstrings what to drop when the wagon is destroyed
has_inventory = false
^- If this wagon has an inventory. The inventory is saved with the wagon.
^- the following settings are ignored if not.
inventory_list_sizes = {
box=8*6,
},
^- List of assignments of type list_name=size.
^- For every entry, an inventory list is created with the specified size.
get_inventory_formspec = function(self, player_name)
return "<a formspec>"
end,
^- Function that should return the formspec to be displayed when <player> requests to open the wagon's inventory
^- Use "list[detached:advtrains_wgn_"..self.unique_id..";<list_name>;<X>,<Y>;<W>,<H>;<Start>]" to display a wagon's inventory list.
custom_on_step = function(self, dtime) end
^- optional: Execute custom code on every step
custom_on_activate = function(self, dtime_s) end
^- optional: Execute custom code on activate. Staticdata does not need to be saved and restored since all properties written in 'self' are preserved over unloads.
update_animation = function(self, velocity) end
^- optional: Function that is called whenever the train's velocity changes or every 2 seconds. Used to call 'self.object:update_animation()' if needed.
}
# Notes on wagons
- Every wagon has the field 'unique_id' which assigns each wagon a random id.
- All properties written in the lua entity (self) are saved and restored automatically. Minetest's internal staticdata is only used to save the unique_id of the wagon, which serves as a key in an externally saved table.
- Assuming Z Axis as the axis parallel to the tracks and Y Axis as the one pointing into the sky, wagon models should be dimensioned in a way that:
- their origin is centered in X and Z direction
- their origin lies 0.5 units above the bottom of the model
- the overall extent in X and Y direction is <=3 units
- wagon_span is then the distance between the model origin and the Z axis extent.
### Tracks
Most modders will be satisfied with the built-in tracks. If cog railways, maglev trains and mine trains are added, it is necessary to understand the definition of tracks. Although the tracks API is there, explaining it would require more effort than me creating the wanted definitions myself. Contact me if you need to register your own rails using my registration functions.
However, it is still possible to register single rails by understanding the node properties of rails.
minetest.register_node(nodename, {
... usual node definition ...
groups = {
advtrains_track_<tracktype>=1
^- this group tells that the node is a track
not_blocking_trains=1,
^- this group tells that the node should not block trains although it's walkable.
},
connect1 = 0,
connect2 = 8,
^- These values tell the direction (horizontal) the rail ends are pointing to. 0 means +Z, then rotation values increase clockwise. For a translation of directions to positions see helpers.lua.
rely1=0,
rely2=0,
^- the Y height of the rail end 1/2. A value of >=1 means that the rail end points to the next y layer at rely-1
railheight=0,
^- the height value of this rail that is saved in the path. usually the median of rely1 and rely2.
can_dig=function(pos)
return not advtrains.is_train_at_pos(pos)
end,
after_dig_node=function(pos)
advtrains.invalidate_all_paths()
advtrains.reset_trackdb_position(pos)
end,
after_place_node=function(pos)
advtrains.reset_trackdb_position(pos)
end,
^- the code in these 3 default minetest API functions is required for advtrains to work, however you can add your own code
advtrains = {
on_train_enter=function(pos, train_id) end
^- called when a train enters the rail
on_train_leave=function(pos, train_id) end
^- called when a train leaves the rail
}
})

274
advtrains/advtrains/atc.lua Normal file
View File

@ -0,0 +1,274 @@
--atc.lua
--registers and controls the ATC system
local atc={}
-- ATC persistence table. advtrains.atc is created by init.lua when it loads the save file.
atc.controllers = {}
function atc.load_data(data)
atc.controllers = data and data.controllers or {}
end
function atc.save_data()
return atc.controllers
end
--contents: {command="...", arrowconn=0-15 where arrow points}
--call from advtrains.detector subprogram
function atc.trigger_controller_train_enter(pos, train_id)
atc.send_command(pos)
end
--general
function atc.send_command(pos)
local pts=minetest.pos_to_string(pos)
if atc.controllers[pts] then
--atprint("Called send_command at "..pts)
local train_id = advtrains.detector.on_node[pts]
if train_id then
if advtrains.trains[train_id] then
--atprint("send_command inside if: "..sid(train_id))
atc.train_reset_command(train_id)
local arrowconn=atc.controllers[pts].arrowconn
local train=advtrains.trains[train_id]
for index, ppos in pairs(train.path) do
if vector.equals(advtrains.round_vector_floor_y(ppos), pos) then
advtrains.trains[train_id].atc_arrow =
vector.equals(
advtrains.dirCoordSet(pos, arrowconn),
advtrains.round_vector_floor_y(train.path[index+train.movedir])
)
advtrains.trains[train_id].atc_command=atc.controllers[pts].command
atprint("Sending ATC Command: "..atc.controllers[pts].command)
end
end
end
end
end
return false
end
function atc.train_reset_command(train_id)
advtrains.trains[train_id].atc_command=nil
advtrains.trains[train_id].atc_delay=0
advtrains.trains[train_id].atc_brake_target=nil
advtrains.trains[train_id].atc_wait_finish=nil
advtrains.trains[train_id].atc_arrow=nil
end
--nodes
local idxtrans={static=1, mesecon=2, digiline=3}
local apn_func=function(pos, node)
advtrains.ndb.update(pos, node)
local meta=minetest.get_meta(pos)
if meta then
meta:set_string("infotext", "ATC controller, unconfigured.")
meta:set_string("formspec", atc.get_atc_controller_formspec(pos, meta))
end
end
advtrains.register_tracks("default", {
nodename_prefix="advtrains:dtrack_atc",
texture_prefix="advtrains_dtrack_atc",
models_prefix="advtrains_dtrack_detector",
models_suffix=".b3d",
shared_texture="advtrains_dtrack_rail_atc.png",
description="ATC controller",
formats={},
get_additional_definiton = function(def, preset, suffix, rotation)
return {
after_place_node=apn_func,
after_dig_node=function(pos)
advtrains.invalidate_all_paths()
advtrains.ndb.clear(pos)
local pts=minetest.pos_to_string(pos)
atc.controllers[pts]=nil
end,
on_receive_fields = function(pos, formname, fields, player)
if minetest.is_protected(pos, player:get_player_name()) then
minetest.chat_send_player(player:get_player_name(), "This position is protected!")
return
end
local meta=minetest.get_meta(pos)
if meta then
if not fields.save then
--maybe only the dropdown changed
if fields.mode then
meta:set_string("mode", idxtrans[fields.mode])
meta:set_string("infotext", "ATC controller, mode "..fields.mode.."\n"..( fields.mode=="digiline" and "Channel: "..meta:get_string("channel") or "Command: "..meta:get_string("command") ) )
meta:set_string("formspec", atc.get_atc_controller_formspec(pos, meta))
end
return
end
meta:set_string("mode", idxtrans[fields.mode])
meta:set_string("command", fields.command)
meta:set_string("command_on", fields.command_on)
meta:set_string("channel", fields.channel)
meta:set_string("infotext", "ATC controller, mode "..fields.mode.."\n"..( fields.mode=="digiline" and "Channel: "..meta:get_string("channel") or "Command: "..meta:get_string("command") ) )
meta:set_string("formspec", atc.get_atc_controller_formspec(pos, meta))
local pts=minetest.pos_to_string(pos)
local _, conn1=advtrains.get_rail_info_at(pos, advtrains.all_tracktypes)
atc.controllers[pts]={command=fields.command, arrowconn=conn1}
atc.send_command(pos)
end
end,
}
end
}, advtrains.trackpresets.t_30deg_straightonly)
function atc.get_atc_controller_formspec(pos, meta)
local mode=tonumber(meta:get_string("mode")) or 1
local command=meta:get_string("command")
local command_on=meta:get_string("command_on")
local channel=meta:get_string("channel")
local formspec="size[8,6]"..
"dropdown[0,0;3;mode;static,mesecon,digiline;"..mode.."]"
if mode<3 then
formspec=formspec.."field[0.5,1.5;7,1;command;Command;"..minetest.formspec_escape(command).."]"
if tonumber(mode)==2 then
formspec=formspec.."field[0.5,3;7,1;command_on;Command (on);"..minetest.formspec_escape(command_on).."]"
end
else
formspec=formspec.."field[0.5,1.5;7,1;channel;Digiline channel;"..minetest.formspec_escape(channel).."]"
end
return formspec.."button_exit[0.5,4.5;7,1;save;Save]"
end
--from trainlogic.lua train step
local matchptn={
["SM"]=function(id, train)
train.tarvelocity=train.max_speed
return 2
end,
["S([0-9]+)"]=function(id, train, match)
train.tarvelocity=tonumber(match)
return #match+1
end,
["B([0-9]+)"]=function(id, train, match)
if train.velocity>tonumber(match) then
train.atc_brake_target=tonumber(match)
if train.tarvelocity>train.atc_brake_target then
train.tarvelocity=train.atc_brake_target
end
end
return #match+1
end,
["W"]=function(id, train)
train.atc_wait_finish=true
return 1
end,
["D([0-9]+)"]=function(id, train, match)
train.atc_delay=tonumber(match)
return #match+1
end,
["R"]=function(id, train)
if train.velocity<=0 then
train.movedir=train.movedir*-1
train.atc_arrow = not train.atc_arrow
else
minetest.chat_send_all("ATC Reverse command warning: didn't reverse train!")
end
return 1
end,
}
function atc.execute_atc_command(id, train)
--strip whitespaces
local command=string.match(train.atc_command, "^%s*(.*)$")
if string.match(command, "^%s*$") then
train.atc_command=nil
return
end
--conditional statement?
local is_cond, cond_applies
local cond, rest=string.match(command, "^I([%+%-])(.+)$")
if cond then
is_cond=true
if cond=="+" then
cond_applies=train.atc_arrow
end
if cond=="-" then
cond_applies=not train.atc_arrow
end
else
cond, compare, rest=string.match(command, "^I([<>]=?)([0-9]+)(.+)$")
if cond and compare then
is_cond=true
if cond=="<" then
cond_applies=train.velocity<tonumber(compare)
end
if cond==">" then
cond_applies=train.velocity>tonumber(compare)
end
if cond=="<=" then
cond_applies=train.velocity<=tonumber(compare)
end
if cond==">=" then
cond_applies=train.velocity>=tonumber(compare)
end
end
end
if is_cond then
atprint("Evaluating if statement: "..command)
atprint("Cond: "..(cond or "nil"))
atprint("Applies: "..(cond_applies and "true" or "false"))
atprint("Rest: "..rest)
--find end of conditional statement
local nest, pos, elsepos=0, 1
while nest>=0 do
if pos>#rest then
minetest.chat_send_all("ATC command syntax error: I statement not closed: "..command)
atc.train_reset_command(id)
return
end
local char=string.sub(rest, pos, pos)
if char=="I" then
nest=nest+1
end
if char==";" then
nest=nest-1
end
if nest==0 and char=="E" then
elsepos=pos+0
end
pos=pos+1
end
if not elsepos then elsepos=pos-1 end
if cond_applies then
command=string.sub(rest, 1, elsepos-1)..string.sub(rest, pos)
else
command=string.sub(rest, elsepos+1, pos-2)..string.sub(rest, pos)
end
atprint("Result: "..command)
train.atc_command=command
atc.execute_atc_command(id, train)
return
else
for pattern, func in pairs(matchptn) do
local match=string.match(command, "^"..pattern)
if match then
local patlen=func(id, train, match)
atprint("Executing: "..string.sub(command, 1, patlen))
train.atc_command=string.sub(command, patlen+1)
if train.atc_delay<=0 and not train.atc_wait_finish then
--continue (recursive, cmds shouldn't get too long, and it's a end-recursion.)
atc.execute_atc_command(id, train)
end
return
end
end
end
minetest.chat_send_all("ATC command parse error: "..command)
atc.train_reset_command(id)
end
--move table to desired place
advtrains.atc=atc

View File

@ -9,7 +9,6 @@ wagon
wagons keep their couple entity minetest-internal id inside the field discouple_id. if it refers to nowhere, they will spawn a new one if player is near
]]
local print=function(t, ...) minetest.log("action", table.concat({t, ...}, " ")) minetest.chat_send_all(table.concat({t, ...}, " ")) end
minetest.register_entity("advtrains:discouple", {
@ -32,7 +31,7 @@ minetest.register_entity("advtrains:discouple", {
on_punch=function(self, player)
--only if player owns at least one wagon next to this
local own=player:get_player_name()
if self.wagon.owner and self.wagon.owner~=own then
if self.wagon.owner and self.wagon.owner==own then
local train=advtrains.trains[self.wagon.train_id]
local nextwgn_id=train.trainparts[self.wagon.pos_in_trainparts-1]
for aoi, le in pairs(minetest.luaentities) do
@ -45,8 +44,11 @@ minetest.register_entity("advtrains:discouple", {
end
end
end
advtrains.split_train_at_wagon(self.wagon)--found in trainlogic.lua
self.object:remove()
else
minetest.chat_send_player(own, "You need to own at least one neighboring wagon to destroy this couple.")
end
advtrains.split_train_at_wagon(self.wagon)--found in trainlogic.lua
end,
on_step=function(self, dtime)
local t=os.clock()
@ -67,7 +69,7 @@ minetest.register_entity("advtrains:discouple", {
self.object:setvelocity(velocityvec)
self.updatepct_timer=2
end
printbm("discouple_step", t)
atprintbm("discouple_step", t)
end,
})
@ -120,7 +122,7 @@ minetest.register_entity("advtrains:couple", {
end,
on_step=function(self, dtime)
local t=os.clock()
if not self.train_id_1 or not self.train_id_2 then print("wtf no train ids?")return end
if not self.train_id_1 or not self.train_id_2 then atprint("wtf no train ids?")return end
local train1=advtrains.trains[self.train_id_1]
local train2=advtrains.trains[self.train_id_2]
if not train1 or not train2 or not train1.path or not train2.path or not train1.index or not train2.index then
@ -140,7 +142,7 @@ minetest.register_entity("advtrains:couple", {
else
tp2=advtrains.get_real_index_position(train2.path, advtrains.get_train_end_index(train2))
end
if not tp1 or not tp2 or not (vector.distance(tp1,tp2)<0.5) then
if not tp1 or not tp2 or not (vector.distance(tp1,tp2)<1.5) then
self.object:remove()
return
else
@ -149,6 +151,6 @@ minetest.register_entity("advtrains:couple", {
self.object:setpos(pos_median)
end
end
printbm("couple step", t)
atprintbm("couple step", t)
end,
})

View File

@ -65,39 +65,7 @@ minetest.register_craft({
},
})
--wagons
minetest.register_craft({
output = 'advtrains:newlocomotive',
recipe = {
{'default:steelblock', 'default:steelblock', 'default:steelblock'},
{'default:steelblock', 'dye:black', 'default:steelblock'},
{'default:steelblock', 'default:steelblock', 'default:steelblock'},
},
})
minetest.register_craft({
output = 'advtrains:wagon_default',
recipe = {
{'default:steelblock', 'default:steelblock', 'default:steelblock'},
{'default:steelblock', 'dye:dark_green', 'default:steelblock'},
{'default:steelblock', 'default:steelblock', 'default:steelblock'},
},
})
minetest.register_craft({
output = 'advtrains:wagon_box',
recipe = {
{'default:steelblock', 'default:steelblock', 'default:steelblock'},
{'default:steelblock', 'default:junglewood', 'default:steelblock'},
{'default:steelblock', 'default:steelblock', 'default:steelblock'},
},
})
minetest.register_craft({
output = 'advtrains:subway_wagon',
recipe = {
{'default:steelblock', 'default:steelblock', 'default:steelblock'},
{'default:steelblock', 'dye:yellow', 'default:steelblock'},
{'default:steelblock', 'default:steelblock', 'default:steelblock'},
},
})
--misc_nodes
--crafts for platforms see misc_nodes.lua

View File

@ -24,13 +24,5 @@ minetest.register_tool("advtrains:tunnelborer",
end
end
end,
--[[
^ default: nil
^ Function must return either nil if no item shall be removed from
inventory, or an itemstack to replace the original itemstack.
e.g. itemstack:take_item(); return itemstack
^ Otherwise, the function is free to do what it wants.
^ The default functions handle regular use cases.
]]
}
)
)

View File

@ -1,5 +1,4 @@
--advtrains by orwell96, see readme.txt
local print=function(t) minetest.log("action", t) minetest.chat_send_all(t) end
advtrains.dir_trans_tbl={
[0]={x=0, z=1},
@ -55,34 +54,29 @@ rely1, rely2 tell to which height the connections are pointed to. 1 means it wil
]]
function advtrains.conway(midreal, prev, traintype)--in order prev,mid,return
function advtrains.conway(midreal, prev, drives_on)--in order prev,mid,return
local mid=advtrains.round_vector_floor_y(midreal)
local drives_on=advtrains.all_traintypes[traintype].drives_on
if not advtrains.get_rail_info_at(advtrains.round_vector_floor_y(prev), traintype) then
return nil
end
local midnode_ok, middir1, middir2, midrely1, midrely2=advtrains.get_rail_info_at(advtrains.round_vector_floor_y(mid), traintype)
local midnode_ok, middir1, middir2, midrely1, midrely2=advtrains.get_rail_info_at(mid, drives_on)
if not midnode_ok then
return nil
end
local next, chkdir, chkrely, y_offset
y_offset=0
--print("[advtrains] in order mid1,mid2",middir1,middir2)
--atprint(" in order mid1,mid2",middir1,middir2)
--try if it is dir1
local cor1=advtrains.dirCoordSet(mid, middir2)--<<<<
if math.floor(cor1.x+0.5)==math.floor(prev.x+0.5) and math.floor(cor1.z+0.5)==math.floor(prev.z+0.5) then--this was previous
if cor1.x==prev.x and cor1.z==prev.z then--this was previous
next=advtrains.dirCoordSet(mid, middir1)
if midrely1>=1 then
next.y=next.y+1
--print("[advtrains]found midrely1 to be >=1: next is now "..(next and minetest.pos_to_string(next) or "nil"))
--atprint("found midrely1 to be >=1: next is now "..(next and minetest.pos_to_string(next) or "nil"))
y_offset=1
end
chkdir=middir1
chkrely=midrely1
--print("[advtrains]dir2 applied next pos:",minetest.pos_to_string(next),"(chkdir is ",chkdir,")")
--atprint("dir2 applied next pos:",minetest.pos_to_string(next),"(chkdir is ",chkdir,")")
end
--dir2???
local cor2=advtrains.dirCoordSet(mid, middir1)--<<<<
@ -90,54 +84,54 @@ function advtrains.conway(midreal, prev, traintype)--in order prev,mid,return
next=advtrains.dirCoordSet(mid, middir2)--dir2 wird überprüft, alles gut.
if midrely2>=1 then
next.y=next.y+1
--print("[advtrains]found midrely2 to be >=1: next is now "..(next and minetest.pos_to_string(next) or "nil"))
--atprint("found midrely2 to be >=1: next is now "..(next and minetest.pos_to_string(next) or "nil"))
y_offset=1
end
chkdir=middir2
chkrely=midrely2
--print("[advtrains] dir2 applied next pos:",minetest.pos_to_string(next),"(chkdir is ",chkdir,")")
--atprint(" dir2 applied next pos:",minetest.pos_to_string(next),"(chkdir is ",chkdir,")")
end
--print("[advtrains]dir applied next pos: "..(next and minetest.pos_to_string(next) or "nil").."(chkdir is "..(chkdir or "nil")..", y-offset "..y_offset..")")
--atprint("dir applied next pos: "..(next and minetest.pos_to_string(next) or "nil").."(chkdir is "..(chkdir or "nil")..", y-offset "..y_offset..")")
--is there a next
if not next then
--print("[advtrains]in conway: no next rail(nil), returning!")
atprint("in conway: no next rail(nil), returning!")
return nil
end
local nextnode_ok, nextdir1, nextdir2, nextrely1, nextrely2, nextrailheight=advtrains.get_rail_info_at(advtrains.round_vector_floor_y(next), traintype)
local nextnode_ok, nextdir1, nextdir2, nextrely1, nextrely2, nextrailheight=advtrains.get_rail_info_at(advtrains.round_vector_floor_y(next), drives_on)
--is it a rail?
if(not nextnode_ok) then
--print("[advtrains]in conway: next "..minetest.pos_to_string(next).." not a rail, trying one node below!")
atprint("in conway: next "..minetest.pos_to_string(next).." not a rail, trying one node below!")
next.y=next.y-1
y_offset=y_offset-1
nextnode_ok, nextdir1, nextdir2, nextrely1, nextrely2, nextrailheight=advtrains.get_rail_info_at(advtrains.round_vector_floor_y(next), traintype)
nextnode_ok, nextdir1, nextdir2, nextrely1, nextrely2, nextrailheight=advtrains.get_rail_info_at(advtrains.round_vector_floor_y(next), drives_on)
if(not nextnode_ok) then
--print("[advtrains]in conway: one below "..minetest.pos_to_string(next).." is not a rail either, returning!")
atprint("in conway: one below "..minetest.pos_to_string(next).." is not a rail either, returning!")
return nil
end
end
--is this next rail connecting to the mid?
if not ( (((nextdir1+8)%16)==chkdir and nextrely1==chkrely-y_offset) or (((nextdir2+8)%16)==chkdir and nextrely2==chkrely-y_offset) ) then
--print("[advtrains]in conway: next "..minetest.pos_to_string(next).." not connecting, trying one node below!")
atprint("in conway: next "..minetest.pos_to_string(next).." not connecting, trying one node below!")
next.y=next.y-1
y_offset=y_offset-1
nextnode_ok, nextdir1, nextdir2, nextrely1, nextrely2, nextrailheight=advtrains.get_rail_info_at(advtrains.round_vector_floor_y(next), traintype)
nextnode_ok, nextdir1, nextdir2, nextrely1, nextrely2, nextrailheight=advtrains.get_rail_info_at(advtrains.round_vector_floor_y(next), drives_on)
if(not nextnode_ok) then
--print("[advtrains]in conway: (at connecting if check again) one below "..minetest.pos_to_string(next).." is not a rail either, returning!")
atprint("in conway: (at connecting if check again) one below "..minetest.pos_to_string(next).." is not a rail either, returning!")
return nil
end
if not ( (((nextdir1+8)%16)==chkdir and nextrely1==chkrely) or (((nextdir2+8)%16)==chkdir and nextrely2==chkrely) ) then
--print("[advtrains]in conway: one below "..minetest.pos_to_string(next).." rail not connecting, returning!")
--print("[advtrains] in order mid1,2,next1,2,chkdir "..middir1.." "..middir2.." "..nextdir1.." "..nextdir2.." "..chkdir)
atprint("in conway: one below "..minetest.pos_to_string(next).." rail not connecting, returning!")
atprint(" in order mid1,2,next1,2,chkdir "..middir1.." "..middir2.." "..nextdir1.." "..nextdir2.." "..chkdir)
return nil
end
end
--print("[advtrains]conway found rail.")
--atprint("conway found rail.")
return vector.add(advtrains.round_vector_floor_y(next), {x=0, y=nextrailheight, z=0}), chkdir
end
--TODO use this
@ -177,10 +171,10 @@ function advtrains.minAngleDiffRad(r1, r2)
end
end
function advtrains.dumppath(path)
if not path then print("dumppath: no path(nil)") return end
if not path then atprint("dumppath: no path(nil)") return end
local min=advtrains.minN(path)
local max=advtrains.maxN(path)
for i=min, max do print("["..i.."] "..(path[i] and minetest.pos_to_string(path[i]) or "nil")) end
for i=min, max do atprint("["..i.."] "..(path[i] and minetest.pos_to_string(path[i]) or "nil")) end
end
function advtrains.merge_tables(a, ...)
@ -192,18 +186,18 @@ function advtrains.merge_tables(a, ...)
end
function advtrains.yaw_from_3_positions(prev, curr, next)
local pts=minetest.pos_to_string
--print("p3 "..pts(prev)..pts(curr)..pts(next))
--atprint("p3 "..pts(prev)..pts(curr)..pts(next))
local prev2curr=math.atan2((curr.x-prev.x), (prev.z-curr.z))
local curr2next=math.atan2((next.x-curr.x), (curr.z-next.z))
--print("y3 "..(prev2curr*360/(2*math.pi)).." "..(curr2next*360/(2*math.pi)))
--atprint("y3 "..(prev2curr*360/(2*math.pi)).." "..(curr2next*360/(2*math.pi)))
return prev2curr+(advtrains.minAngleDiffRad(prev2curr, curr2next)/2)
end
function advtrains.get_wagon_yaw(front, first, second, back, pct)
local pts=minetest.pos_to_string
--print("p "..pts(front)..pts(first)..pts(second)..pts(back))
--atprint("p "..pts(front)..pts(first)..pts(second)..pts(back))
local y2=advtrains.yaw_from_3_positions(second, first, front)
local y1=advtrains.yaw_from_3_positions(back, second, first)
--print("y "..(y1*360/(2*math.pi)).." "..(y2*360/(2*math.pi)))
--atprint("y "..(y1*360/(2*math.pi)).." "..(y2*360/(2*math.pi)))
return y1+advtrains.minAngleDiffRad(y1, y2)*pct
end
function advtrains.get_real_index_position(path, index)
@ -247,3 +241,4 @@ function advtrains.deserialize_inventory(sers, inv)
end
return false
end

View File

@ -0,0 +1,180 @@
--advtrains
advtrains = {trains={}, wagon_save={}}
advtrains.modpath = minetest.get_modpath("advtrains")
local function print_concat_table(a)
local str=""
local stra=""
for i=1,50 do
t=a[i]
if t==nil then
stra=stra.."nil "
else
str=str..stra
stra=""
if type(t)=="table" then
if t.x and t.y and t.z then
str=str..minetest.pos_to_string(t)
else
str=str..dump(t)
end
elseif type(t)=="boolean" then
if t then
str=str.."true"
else
str=str.."false"
end
else
str=str..t
end
str=str.." "
end
end
return str
end
--atprint=function() end
atprint=function(t, ...) minetest.log("action", "[advtrains]"..print_concat_table({t, ...})) minetest.chat_send_all("[advtrains]"..print_concat_table({t, ...})) end
sid=function(id) return string.sub(id, -4) end
dofile(advtrains.modpath.."/helpers.lua");
dofile(advtrains.modpath.."/debugitems.lua");
advtrains.meseconrules =
{{x=0, y=0, z=-1},
{x=1, y=0, z=0},
{x=-1, y=0, z=0},
{x=0, y=0, z=1},
{x=1, y=1, z=0},
{x=1, y=-1, z=0},
{x=-1, y=1, z=0},
{x=-1, y=-1, z=0},
{x=0, y=1, z=1},
{x=0, y=-1, z=1},
{x=0, y=1, z=-1},
{x=0, y=-1, z=-1},
{x=0, y=-2, z=0}}
dofile(advtrains.modpath.."/trainlogic.lua")
dofile(advtrains.modpath.."/trainhud.lua")
dofile(advtrains.modpath.."/trackplacer.lua")
dofile(advtrains.modpath.."/tracks.lua")
dofile(advtrains.modpath.."/atc.lua")
dofile(advtrains.modpath.."/wagons.lua")
dofile(advtrains.modpath.."/trackdb_legacy.lua")
dofile(advtrains.modpath.."/nodedb.lua")
dofile(advtrains.modpath.."/couple.lua")
dofile(advtrains.modpath.."/damage.lua")
dofile(advtrains.modpath.."/signals.lua")
dofile(advtrains.modpath.."/misc_nodes.lua")
dofile(advtrains.modpath.."/crafting.lua")
--load/save
advtrains.fpath=minetest.get_worldpath().."/advtrains"
local file, err = io.open(advtrains.fpath, "r")
if not file then
minetest.log("error", " Failed to read advtrains save data from file "..advtrains.fpath..": "..(err or "Unknown Error"))
else
local tbl = minetest.deserialize(file:read("*a"))
if type(tbl) == "table" then
if tbl.version then
--congrats, we have the new save format.
advtrains.trains = tbl.trains
advtrains.wagon_save = tbl.wagon_save
advtrains.ndb.load_data(tbl.ndb)
advtrains.atc.load_data(tbl.atc)
--advtrains.latc.load_data(tbl.latc)
else
--oh no, its the old one...
advtrains.trains=tbl
--load ATC
advtrains.fpath_atc=minetest.get_worldpath().."/advtrains_atc"
local file, err = io.open(advtrains.fpath_atc, "r")
if not file then
local er=err or "Unknown Error"
atprint("Failed loading advtrains atc save file "..er)
else
local tbl = minetest.deserialize(file:read("*a"))
if type(tbl) == "table" then
advtrains.atc.controllers=tbl.controllers
end
file:close()
end
--load wagon saves
advtrains.fpath_ws=minetest.get_worldpath().."/advtrains_wagon_save"
local file, err = io.open(advtrains.fpath_ws, "r")
if not file then
local er=err or "Unknown Error"
atprint("Failed loading advtrains save file "..er)
else
local tbl = minetest.deserialize(file:read("*a"))
if type(tbl) == "table" then
advtrains.wagon_save=tbl
end
file:close()
end
end
else
minetest.log("error", " Failed to deserialize advtrains save data: Not a table!")
end
file:close()
end
advtrains.save = function()
--atprint("saving")
advtrains.invalidate_all_paths()
-- update wagon saves
for _,wagon in pairs(minetest.luaentities) do
if wagon.is_wagon and wagon.initialized then
wagon:get_staticdata()
end
end
--cross out userdata
for w_id, data in pairs(advtrains.wagon_save) do
data.name=nil
data.object=nil
if data.driver then
data.driver_name=data.driver:get_player_name()
data.driver=nil
else
data.driver_name=nil
end
if data.discouple then
data.discouple.object:remove()
data.discouple=nil
end
end
--versions:
-- 1 - Initial new save format.
local save_tbl={
trains = advtrains.trains,
wagon_save = advtrains.wagon_save,
atc = advtrains.atc.save_data(),
--latc = advtrains.latc.save_data(),
ndb = advtrains.ndb.save_data(),
version = 1,
}
local datastr = minetest.serialize(save_tbl)
if not datastr then
minetest.log("error", " Failed to serialize advtrains save data!")
return
end
local file, err = io.open(advtrains.fpath, "w")
if err then
minetest.log("error", " Failed to write advtrains save data to file "..advtrains.fpath..": "..(err or "Unknown Error"))
return
end
file:write(datastr)
file:close()
end
minetest.register_on_shutdown(advtrains.save)

View File

@ -0,0 +1,166 @@
-------------
--LUA ATC controllers
local latc={}
function latc.load_data(data)
end
function latc.save_data()
return stuff
end
latc.data
latc.env_cdata
latc.init_code=""
latc.step_code=""
advtrains.fpath_latc=minetest.get_worldpath().."/advtrains_latc"
local file, err = io.open(advtrains.fpath_atc, "r")
if not file then
local er=err or "Unknown Error"
atprint("Failed loading advtrains latc save file "..er)
else
local tbl = minetest.deserialize(file:read("*a"))
if type(tbl) == "table" then
atc.controllers=tbl.controllers
end
file:close()
end
function latc.save()
local datastr = minetest.serialize({controllers = atc.controllers})
if not datastr then
minetest.log("error", " Failed to serialize latc data!")
return
end
local file, err = io.open(advtrains.fpath_atc, "w")
if err then
return err
end
file:write(datastr)
file:close()
end
--Privilege
--Only trusted players should be enabled to build stuff which can break the server.
--If I later decide to have multiple environments ('data' tables), I better store an owner for every controller for future reference.
minetest.register_privilege("advtrains_lua_atc", { description = "Player can place and modify LUA ATC components. Grant with care! Allows to execute bad LUA code.", give_to_singleplayer = false, default= false })
--Environment
--Code from mesecons_luacontroller (credit goes to Jeija and mesecons contributors)
local safe_globals = {
"assert", "error", "ipairs", "next", "pairs", "select",
"tonumber", "tostring", "type", "unpack", "_VERSION"
}
local function safe_print(param)
print(dump(param))
end
local function safe_date()
return(os.date("*t",os.time()))
end
-- string.rep(str, n) with a high value for n can be used to DoS
-- the server. Therefore, limit max. length of generated string.
local function safe_string_rep(str, n)
if #str * n > mesecon.setting("luacontroller_string_rep_max", 64000) then
debug.sethook() -- Clear hook
error("string.rep: string length overflow", 2)
end
return string.rep(str, n)
end
-- string.find with a pattern can be used to DoS the server.
-- Therefore, limit string.find to patternless matching.
local function safe_string_find(...)
if (select(4, ...)) ~= true then
debug.sethook() -- Clear hook
error("string.find: 'plain' (fourth parameter) must always be true in a LuaController")
end
return string.find(...)
end
latc.static_env = {
print = safe_print,
string = {
byte = string.byte,
char = string.char,
format = string.format,
len = string.len,
lower = string.lower,
upper = string.upper,
rep = safe_string_rep,
reverse = string.reverse,
sub = string.sub,
find = safe_string_find,
},
math = {
abs = math.abs,
acos = math.acos,
asin = math.asin,
atan = math.atan,
atan2 = math.atan2,
ceil = math.ceil,
cos = math.cos,
cosh = math.cosh,
deg = math.deg,
exp = math.exp,
floor = math.floor,
fmod = math.fmod,
frexp = math.frexp,
huge = math.huge,
ldexp = math.ldexp,
log = math.log,
log10 = math.log10,
max = math.max,
min = math.min,
modf = math.modf,
pi = math.pi,
pow = math.pow,
rad = math.rad,
random = math.random,
sin = math.sin,
sinh = math.sinh,
sqrt = math.sqrt,
tan = math.tan,
tanh = math.tanh,
},
table = {
concat = table.concat,
insert = table.insert,
maxn = table.maxn,
remove = table.remove,
sort = table.sort,
},
os = {
clock = os.clock,
difftime = os.difftime,
time = os.time,
datetable = safe_date,
},
}
latc.static_env._G = env
for _, name in pairs(safe_globals) do
latc.static_env[name] = _G[name]
end
--The environment all code calls get is a proxy table with a metatable.
--When an index is read:
-- Look in static_env
-- Look in volatile_env (user_written functions and userdata)
-- Look in saved_env (everything that's not a function or userdata)
--when an index is written:
-- If in static_env, do not allow
-- if function or userdata, volatile_env
-- if table, see below
-- else, save in saved_env
advtrains.latc=latc

View File

@ -3,7 +3,7 @@
function advtrains.register_platform(preset)
local ndef=minetest.registered_nodes[preset]
if not ndef then
minetest.log("warning", "[advtrains] register_platform couldn't find preset node "..preset)
minetest.log("warning", " register_platform couldn't find preset node "..preset)
return
end
local btex=ndef.tiles

View File

@ -0,0 +1,232 @@
--nodedb.lua
--database of all nodes that have 'save_in_nodedb' field set to true in node definition
--serialization format:
--(6byte poshash) (2byte contentid)
--contentid := (14bit nodeid, 2bit param2)
local function hash_to_bytes(x)
local aH = math.floor(x / 1099511627776) % 256;
local aL = math.floor(x / 4294967296) % 256;
local bH = math.floor(x / 16777216) % 256;
local bL = math.floor(x / 65536) % 256;
local cH = math.floor(x / 256) % 256;
local cL = math.floor(x ) % 256;
return(string.char(aH, aL, bH, bL, cH, cL));
end
local function cid_to_bytes(x)
local cH = math.floor(x / 256) % 256;
local cL = math.floor(x ) % 256;
return(string.char(cH, cL));
end
local function bytes_to_hash(bytes)
local t={string.byte(bytes,1,-1)}
local n =
t[1] * 1099511627776 +
t[2] * 4294967296 +
t[3] * 16777216 +
t[4] * 65536 +
t[5] * 256 +
t[6]
return n
end
local function bytes_to_cid(bytes)
local t={string.byte(bytes,1,-1)}
local n =
t[1] * 256 +
t[2]
return n
end
local function l2b(x)
return x%4
end
local function u14b(x)
return math.floor(x/4)
end
local ndb={}
--local variables for performance
local ndb_nodeids={}
local ndb_nodes={}
--load
--nodeids get loaded by advtrains init.lua and passed here
function ndb.load_data(data)
ndb_nodeids = data and data.nodeids or {}
end
local path=minetest.get_worldpath().."/advtrains_ndb"
local file, err = io.open(path, "r")
if not file then
atprint("load ndb failed: ", err or "Unknown Error")
else
local cnt=0
local hst=file:read(6)
local cid=file:read(2)
while hst and #hst==6 and cid and #cid==2 do
ndb_nodes[bytes_to_hash(hst)]=bytes_to_cid(cid)
cnt=cnt+1
hst=file:read(6)
cid=file:read(2)
end
atprint("nodedb: read", cnt, "nodes.")
file:close()
end
--save
function ndb.save_data()
local file, err = io.open(path, "w")
if not file then
atprint("load ndb failed: ", err or "Unknown Error")
else
for hash, cid in pairs(ndb_nodes) do
file:write(hash_to_bytes(hash))
file:write(cid_to_bytes(cid))
end
file:close()
end
return {nodeids = ndb_nodeids}
end
--function to get node. track database is not helpful here.
function ndb.get_node_or_nil(pos)
local node=minetest.get_node_or_nil(pos)
if node then
return node
else
--maybe we have the node in the database...
local cid=ndb_nodes[minetest.hash_node_position(pos)]
if cid then
local nodeid = ndb_nodeids[u14b(cid)]
if nodeid then
--atprint("ndb.get_node_or_nil",pos,"found node",nodeid,"cid",cid,"par2",l2b(cid))
return {name=nodeid, param2 = l2b(cid)}
end
end
end
atprint("ndb.get_node_or_nil",pos,"not found")
end
function ndb.get_node(pos)
local n=ndb.get_node_or_nil(pos)
if not n then
return {name="ignore", param2=0}
end
return n
end
function ndb.swap_node(pos, node)
minetest.swap_node(pos, node)
ndb.update(pos, node)
end
function ndb.update(pos, pnode)
local node = pnode or minetest.get_node_or_nil(pos)
if not node or node.name=="ignore" then return end
if minetest.registered_nodes[node.name] and minetest.registered_nodes[node.name].groups.save_in_nodedb then
local nid
for tnid, nname in pairs(ndb_nodeids) do
if nname==node.name then
nid=tnid
end
end
if not nid then
nid=#ndb_nodeids+1
ndb_nodeids[nid]=node.name
end
local hash = minetest.hash_node_position(pos)
ndb_nodes[hash] = (nid * 4) + (l2b(node.param2 or 0))
--atprint("nodedb: updating node", pos, "stored nid",nid,"assigned",ndb_nodeids[nid],"resulting cid",ndb_nodes[hash])
else
--at this position there is no longer a node that needs to be tracked.
local hash = minetest.hash_node_position(pos)
ndb_nodes[hash] = nil
end
end
function ndb.clear(pos)
local hash = minetest.hash_node_position(pos)
ndb_nodes[hash] = nil
end
--get_node with pseudoload. now we only need track data, so we can use the trackdb as second fallback
--nothing new will be saved inside the trackdb.
--returns:
--true, conn1, conn2, rely1, rely2, railheight in case everything's right.
--false if it's not a rail or the train does not drive on this rail, but it is loaded or
--nil if the node is neither loaded nor in trackdb
--the distraction between false and nil will be needed only in special cases.(train initpos)
function advtrains.get_rail_info_at(pos, drives_on)
local rdp=advtrains.round_vector_floor_y(pos)
local node=ndb.get_node_or_nil(rdp)
--still no node?
--advtrains.trackdb is nil when there's no data available.
if not node then
if advtrains.trackdb then
--try raildb (see trackdb_legacy.lua)
local dbe=(advtrains.trackdb[rdp.y] and advtrains.trackdb[rdp.y][rdp.x] and advtrains.trackdb[rdp.y][rdp.x][rdp.z])
if dbe then
for tt,_ in pairs(drives_on) do
if not dbe.tracktype or tt==dbe.tracktype then
return true, dbe.conn1, dbe.conn2, dbe.rely1 or 0, dbe.rely2 or 0, dbe.railheight or 0
end
end
end
end
return nil
end
local nodename=node.name
if(not advtrains.is_track_and_drives_on(nodename, drives_on)) then
return false
end
local conn1, conn2, rely1, rely2, railheight, tracktype=advtrains.get_track_connections(node.name, node.param2)
return true, conn1, conn2, rely1, rely2, railheight
end
minetest.register_abm({
name = "advtrains:nodedb_on_load_update",
nodenames = {"group:save_in_nodedb"},
run_at_every_load = true,
action = function(pos, node)
local cid=ndb_nodes[minetest.hash_node_position(pos)]
if cid then
--if in database, detect changes and apply.
local nodeid = ndb_nodeids[u14b(cid)]
local param2 = l2b(cid)
if not nodeid then
--something went wrong
atprint("nodedb: lbm nid not found", pos, "with nid", u14b(cid), "param2", param2, "cid is", cid)
ndb.update(pos, node)
else
if (nodeid~=node.name or param2~=node.param2) then
atprint("nodedb: lbm replaced", pos, "with nodeid", nodeid, "param2", param2, "cid is", cid)
minetest.swap_node(pos, {name=nodeid, param2 = param2})
end
end
else
--if not in database, take it.
atprint("nodedb: ", pos, "not in database")
ndb.update(pos, node)
end
end,
interval=10,
chance=1,
})
minetest.register_on_dignode(function(pos, oldnode, digger)
ndb.clear(pos)
end)
function ndb.t()
return ndb_nodes[141061759008906]
end
advtrains.ndb=ndb

View File

@ -29,14 +29,15 @@ for r,f in pairs({on="off", off="on"}) do
choppy=3,
not_blocking_trains=1,
not_in_creative_inventory=crea,
save_in_nodedb=1,
},
mesecons = {effector = {
["action_"..f] = function (pos, node)
minetest.swap_node(pos, {name = "advtrains:retrosignal_"..f..rotation, param2 = node.param2})
advtrains.np.swap_node(pos, {name = "advtrains:retrosignal_"..f..rotation, param2 = node.param2})
end
}},
on_rightclick=function(pos, node, clicker)
minetest.swap_node(pos, {name = "advtrains:retrosignal_"..f..rotation, param2 = node.param2})
advtrains.np.swap_node(pos, {name = "advtrains:retrosignal_"..f..rotation, param2 = node.param2})
end,
})
advtrains.trackplacer.add_worked("advtrains:retrosignal", r, rotation, nil)
@ -59,6 +60,7 @@ for r,f in pairs({on="off", off="on"}) do
choppy=3,
not_blocking_trains=1,
not_in_creative_inventory=crea,
save_in_nodedb=1,
},
light_source = 1,
sunlight_propagates=true,

View File

Before

Width:  |  Height:  |  Size: 265 B

After

Width:  |  Height:  |  Size: 265 B

View File

Before

Width:  |  Height:  |  Size: 307 B

After

Width:  |  Height:  |  Size: 307 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

Before

Width:  |  Height:  |  Size: 193 B

After

Width:  |  Height:  |  Size: 193 B

View File

Before

Width:  |  Height:  |  Size: 8.3 KiB

After

Width:  |  Height:  |  Size: 8.3 KiB

View File

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

Before

Width:  |  Height:  |  Size: 856 B

After

Width:  |  Height:  |  Size: 856 B

View File

Before

Width:  |  Height:  |  Size: 5.7 KiB

After

Width:  |  Height:  |  Size: 5.7 KiB

View File

Before

Width:  |  Height:  |  Size: 5.7 KiB

After

Width:  |  Height:  |  Size: 5.7 KiB

View File

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 33 KiB

View File

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 33 KiB

View File

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 32 KiB

View File

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

View File

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 39 KiB

View File

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 33 KiB

View File

Before

Width:  |  Height:  |  Size: 45 KiB

After

Width:  |  Height:  |  Size: 45 KiB

View File

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 32 KiB

View File

Before

Width:  |  Height:  |  Size: 45 KiB

After

Width:  |  Height:  |  Size: 45 KiB

View File

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 33 KiB

View File

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 46 KiB

Some files were not shown because too many files have changed in this diff Show More