0e1d836062f86ed10ab5117cbfbae33748b509c0
[advtrains.git] / advtrains / advtrains / nodedb.lua
1 --nodedb.lua
2 --database of all nodes that have 'save_in_nodedb' field set to true in node definition
3
4
5
6 --serialization format:
7 --(6byte poshash) (2byte contentid)
8 --contentid := (14bit nodeid, 2bit param2)
9
10 local function hash_to_bytes(x)
11 local aH = math.floor(x / 1099511627776) % 256;
12 local aL = math.floor(x / 4294967296) % 256;
13 local bH = math.floor(x / 16777216) % 256;
14 local bL = math.floor(x / 65536) % 256;
15 local cH = math.floor(x / 256) % 256;
16 local cL = math.floor(x ) % 256;
17 return(string.char(aH, aL, bH, bL, cH, cL));
18 end
19 local function cid_to_bytes(x)
20 local cH = math.floor(x / 256) % 256;
21 local cL = math.floor(x ) % 256;
22 return(string.char(cH, cL));
23 end
24 local function bytes_to_hash(bytes)
25 local t={string.byte(bytes,1,-1)}
26 local n =
27 t[1] * 1099511627776 +
28 t[2] * 4294967296 +
29 t[3] * 16777216 +
30 t[4] * 65536 +
31 t[5] * 256 +
32 t[6]
33 return n
34 end
35 local function bytes_to_cid(bytes)
36 local t={string.byte(bytes,1,-1)}
37 local n =
38 t[1] * 256 +
39 t[2]
40 return n
41 end
42 local function l2b(x)
43 return x%4
44 end
45 local function u14b(x)
46 return math.floor(x/4)
47 end
48 local ndb={}
49
50 --local variables for performance
51 local ndb_nodeids={}
52 local ndb_nodes={}
53
54 --load
55 --nodeids get loaded by advtrains init.lua and passed here
56 function ndb.load_data(data)
57 ndb_nodeids = data and data.nodeids or {}
58 end
59
60 local path=minetest.get_worldpath().."/advtrains_ndb"
61
62 local file, err = io.open(path, "r")
63 if not file then
64 atprint("load ndb failed: ", err or "Unknown Error")
65 else
66 local cnt=0
67 local hst=file:read(6)
68 local cid=file:read(2)
69 while hst and #hst==6 and cid and #cid==2 do
70 ndb_nodes[bytes_to_hash(hst)]=bytes_to_cid(cid)
71 cnt=cnt+1
72 hst=file:read(6)
73 cid=file:read(2)
74 end
75 atprint("nodedb: read", cnt, "nodes.")
76 file:close()
77 end
78
79 --save
80 function ndb.save_data()
81 local file, err = io.open(path, "w")
82 if not file then
83 atprint("load ndb failed: ", err or "Unknown Error")
84 else
85 for hash, cid in pairs(ndb_nodes) do
86 file:write(hash_to_bytes(hash))
87 file:write(cid_to_bytes(cid))
88 end
89 file:close()
90 end
91 return {nodeids = ndb_nodeids}
92 end
93
94 --function to get node. track database is not helpful here.
95 function ndb.get_node_or_nil(pos)
96 local node=minetest.get_node_or_nil(pos)
97 if node then
98 return node
99 else
100 --maybe we have the node in the database...
101 local cid=ndb_nodes[minetest.hash_node_position(pos)]
102 if cid then
103 local nodeid = ndb_nodeids[u14b(cid)]
104 if nodeid then
105 --atprint("ndb.get_node_or_nil",pos,"found node",nodeid,"cid",cid,"par2",l2b(cid))
106 return {name=nodeid, param2 = l2b(cid)}
107 end
108 end
109 end
110 atprint("ndb.get_node_or_nil",pos,"not found")
111 end
112 function ndb.get_node(pos)
113 local n=ndb.get_node_or_nil(pos)
114 if not n then
115 return {name="ignore", param2=0}
116 end
117 return n
118 end
119
120 function ndb.swap_node(pos, node)
121 minetest.swap_node(pos, node)
122 ndb.update(pos, node)
123 end
124
125 function ndb.update(pos, pnode)
126 local node = pnode or minetest.get_node_or_nil(pos)
127 if not node or node.name=="ignore" then return end
128 if minetest.registered_nodes[node.name] and minetest.registered_nodes[node.name].groups.save_in_nodedb then
129 local nid
130 for tnid, nname in pairs(ndb_nodeids) do
131 if nname==node.name then
132 nid=tnid
133 end
134 end
135 if not nid then
136 nid=#ndb_nodeids+1
137 ndb_nodeids[nid]=node.name
138 end
139 local hash = minetest.hash_node_position(pos)
140 ndb_nodes[hash] = (nid * 4) + (l2b(node.param2 or 0))
141 --atprint("nodedb: updating node", pos, "stored nid",nid,"assigned",ndb_nodeids[nid],"resulting cid",ndb_nodes[hash])
142 else
143 --at this position there is no longer a node that needs to be tracked.
144 local hash = minetest.hash_node_position(pos)
145 ndb_nodes[hash] = nil
146 end
147 end
148
149 function ndb.clear(pos)
150 local hash = minetest.hash_node_position(pos)
151 ndb_nodes[hash] = nil
152 end
153
154
155 --get_node with pseudoload. now we only need track data, so we can use the trackdb as second fallback
156 --nothing new will be saved inside the trackdb.
157 --returns:
158 --true, conn1, conn2, rely1, rely2, railheight in case everything's right.
159 --false if it's not a rail or the train does not drive on this rail, but it is loaded or
160 --nil if the node is neither loaded nor in trackdb
161 --the distraction between false and nil will be needed only in special cases.(train initpos)
162 function advtrains.get_rail_info_at(pos, drives_on)
163 local rdp=advtrains.round_vector_floor_y(pos)
164
165 local node=ndb.get_node_or_nil(rdp)
166
167 --still no node?
168 --advtrains.trackdb is nil when there's no data available.
169 if not node then
170 if advtrains.trackdb then
171 --try raildb (see trackdb_legacy.lua)
172 local dbe=(advtrains.trackdb[rdp.y] and advtrains.trackdb[rdp.y][rdp.x] and advtrains.trackdb[rdp.y][rdp.x][rdp.z])
173 if dbe then
174 for tt,_ in pairs(drives_on) do
175 if not dbe.tracktype or tt==dbe.tracktype then
176 return true, dbe.conn1, dbe.conn2, dbe.rely1 or 0, dbe.rely2 or 0, dbe.railheight or 0
177 end
178 end
179 end
180 end
181 return nil
182 end
183 local nodename=node.name
184 if(not advtrains.is_track_and_drives_on(nodename, drives_on)) then
185 return false
186 end
187 local conn1, conn2, rely1, rely2, railheight, tracktype=advtrains.get_track_connections(node.name, node.param2)
188
189 return true, conn1, conn2, rely1, rely2, railheight
190 end
191
192
193 minetest.register_abm({
194 name = "advtrains:nodedb_on_load_update",
195 nodenames = {"group:save_in_nodedb"},
196 run_at_every_load = true,
197 action = function(pos, node)
198 local cid=ndb_nodes[minetest.hash_node_position(pos)]
199 if cid then
200 --if in database, detect changes and apply.
201 local nodeid = ndb_nodeids[u14b(cid)]
202 local param2 = l2b(cid)
203 if not nodeid then
204 --something went wrong
205 atprint("nodedb: lbm nid not found", pos, "with nid", u14b(cid), "param2", param2, "cid is", cid)
206 ndb.update(pos, node)
207 else
208 if (nodeid~=node.name or param2~=node.param2) then
209 atprint("nodedb: lbm replaced", pos, "with nodeid", nodeid, "param2", param2, "cid is", cid)
210 minetest.swap_node(pos, {name=nodeid, param2 = param2})
211 end
212 end
213 else
214 --if not in database, take it.
215 atprint("nodedb: ", pos, "not in database")
216 ndb.update(pos, node)
217 end
218 end,
219 interval=10,
220 chance=1,
221 })
222
223 minetest.register_on_dignode(function(pos, oldnode, digger)
224 ndb.clear(pos)
225 end)
226
227 function ndb.t()
228 return ndb_nodes[141061759008906]
229 end
230
231 advtrains.ndb=ndb
232