Merged priv
5
Makefile
|
@ -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
|
||||
|
|
BIN
advtrains.zip
|
@ -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
|
||||
}
|
||||
})
|
|
@ -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
|
|
@ -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,
|
||||
})
|
|
@ -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
|
|
@ -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.
|
||||
]]
|
||||
}
|
||||
)
|
||||
)
|
|
@ -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
|
||||
|
|
@ -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)
|
||||
|
||||
|
||||
|
||||
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
|
@ -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,
|
Before Width: | Height: | Size: 265 B After Width: | Height: | Size: 265 B |
Before Width: | Height: | Size: 307 B After Width: | Height: | Size: 307 B |
After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 2.7 KiB |
After Width: | Height: | Size: 2.8 KiB |
After Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.4 KiB |
Before Width: | Height: | Size: 193 B After Width: | Height: | Size: 193 B |
Before Width: | Height: | Size: 8.3 KiB After Width: | Height: | Size: 8.3 KiB |
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 856 B After Width: | Height: | Size: 856 B |
Before Width: | Height: | Size: 5.7 KiB After Width: | Height: | Size: 5.7 KiB |
Before Width: | Height: | Size: 5.7 KiB After Width: | Height: | Size: 5.7 KiB |
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 33 KiB |
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 33 KiB |
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 39 KiB |
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 33 KiB |
Before Width: | Height: | Size: 45 KiB After Width: | Height: | Size: 45 KiB |
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
Before Width: | Height: | Size: 45 KiB After Width: | Height: | Size: 45 KiB |
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 33 KiB |
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 46 KiB |