aboutsummaryrefslogtreecommitdiff
path: root/advtrains_luaautomation/active_common.lua
blob: 311ec04c4e2337cdf9c28d5a32cc9a61b8880b3a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
-- Get current translator
local S = atlatc.translate

local ac = {nodes={}}

function ac.load(data)
	if data then
		ac.nodes=data.nodes
	end
end
function ac.save()
	return {nodes = ac.nodes}
end

function ac.get_player_default_environment(player)
	local meta = player:get_meta()
	local env = meta:get_string("atlatc_default_env")

	if not atlatc.envs[env] then
		-- Set the first key for the user as default environment
		for envname, _ in pairs(atlatc.envs) do
			meta:set_string("atlatc_default_env", envname)
			return envname
		end

		-- We don't have any environments
		return nil
	end

	return env
end
function ac.set_player_default_environment(player, env)
	if atlatc.envs[env] then
		local meta = player:get_meta()
		meta:set_string("atlatc_default_env", env)
		return true
	end
	return false
end

function ac.after_place_node(pos, player)
	local env = ac.get_player_default_environment(player)
	if env then
		local ph = minetest.pos_to_string(pos)
		ac.nodes[ph] = { env = env }
	end

	local meta=minetest.get_meta(pos)
	meta:set_string("formspec", ac.getform(pos, meta))
	meta:set_string("infotext", S("Unconfigured LuaATC component"))
end
function ac.getform(pos, meta_p)
	local meta = meta_p or minetest.get_meta(pos)
	local envs_asvalues={}
	
	local ph=minetest.pos_to_string(pos)
	local nodetbl = ac.nodes[ph]
	local env, code, err = nil, "", ""
	if nodetbl then
		code=nodetbl.code or ""
		err=nodetbl.err or ""
		env=nodetbl.env or ""
	end
	local sel = 1
	for n,_ in pairs(atlatc.envs) do
		envs_asvalues[#envs_asvalues+1]=minetest.formspec_escape(n)
		if n==env then
			sel=#envs_asvalues
		end
	end
	local form = "size["..atlatc.CODE_FORM_SIZE.."]"
		.."style[code;font=mono]"
		.."label[0,-0.1;"..S("LuaATC Environment").."]"
		.."dropdown[0,0.3;3;env;"..table.concat(envs_asvalues, ",")..";"..sel.."]"
		.."button[5,0.2;2,1;save;"..S("Save").."]"
		.."button[7,0.2;3,1;cle;"..S("Clear Local Environment").."]"
		.."textarea[0.3,1.5;"..atlatc.CODE_FORM_SIZE..";code;"..S("Code")..";"..minetest.formspec_escape(code).."]"
		.."label["..atlatc.CODE_FORM_ERRLABELPOS..";"..err.."]"
	return form
end

function ac.after_dig_node(pos, node, player)
	advtrains.invalidate_all_paths(pos)
	advtrains.ndb.clear(pos)
	atlatc.interrupt.clear_ints_at_pos(pos)
	if advtrains.lines and advtrains.lines.sched then
		advtrains.lines.sched.discard_all(advtrains.encode_pos(pos))
	end
	local ph=minetest.pos_to_string(pos)
	ac.nodes[ph]=nil
end

function ac.on_receive_fields(pos, formname, fields, player)
	if not minetest.check_player_privs(player:get_player_name(), {atlatc=true}) then
		minetest.chat_send_player(player:get_player_name(), S("You are not allowed to configure this LuaATC component without the @1 privilege.", "atlatc"))
		return
	end
	
	local meta=minetest.get_meta(pos)
	local ph=minetest.pos_to_string(pos)
	local nodetbl = ac.nodes[ph] or {}
	--if fields.quit then return end
	if fields.env then
		nodetbl.env=fields.env
	end
	if fields.code then
		nodetbl.code=fields.code
	end
	if fields.save then
		-- reset certain things
		nodetbl.err=nil
		if advtrains.lines and advtrains.lines.sched then
			-- discard all schedules for this node
			advtrains.lines.sched.discard_all(advtrains.encode_pos(pos))
		end

		-- Parse code headers
		--[[
		Valid code headers:
		* ADVT-Environment-Name: <env name>
		* ADVT-Approach-Callback-Mode: <0/1/2>
		]]
		local errmsg_lines = {}
		local code_headers = {}
		for k, v in nodetbl.code:gmatch("%-%- ADVT%-([%w%-]+): ([^\n\r]*)") do
			code_headers[k] = v
		end

		if code_headers["Environment-Name"] then
			local env = code_headers["Environment-Name"]:trim()
			if not atlatc.envs[env] then
				errmsg_lines[#errmsg_lines+1] = S("Invalid environment name in header: @1", env)
			else
				nodetbl.env = env
			end
			code_headers["Environment-Name"] = nil
		end

		if code_headers["Approach-Callback-Mode"] then
			local mode_raw = code_headers["Approach-Callback-Mode"]:trim()
			if mode_raw == "0" or mode_raw == "1" or mode_raw == "2" then
				nodetbl.data = nodetbl.data or {}
				nodetbl.data.__approach_callback_mode = tonumber(mode_raw)
			else
				errmsg_lines[#errmsg_lines+1] = S("Invalid approach callback mode in header: @1", mode_raw)
			end
			code_headers["Approach-Callback-Mode"] = nil
		end

		local invalid_headers = {}
		for k in pairs(code_headers) do
			invalid_headers[#invalid_headers+1] = k
		end
		if #invalid_headers > 0 then
			table.sort(invalid_headers)
			errmsg_lines[#errmsg_lines+1] = S("Invalid attribute headers: @1", table.concat(invalid_headers, ", "))
		end

		if #errmsg_lines > 0 then
			nodetbl.err = core.formspec_escape(table.concat(errmsg_lines, "; "))
		end
	end
	if fields.cle then
		nodetbl.data={}
	end
	
	ac.nodes[ph]=nodetbl
	
	meta:set_string("formspec", ac.getform(pos, meta))
	if nodetbl.env then
		meta:set_string("infotext", S("LuaATC component assigned to environment '@1'", nodetbl.env))
	else
		meta:set_string("infotext", S("LuaATC component assigned to an invalid environment"))
	end
end

function ac.run_in_env(pos, evtdata, customfct_p, ignore_no_code)
	local ph=minetest.pos_to_string(pos)
	local nodetbl = ac.nodes[ph]
	if not nodetbl then
		atwarn("LuaATC component at",ph,": Data not in memory! Please visit component and click 'Save'!")
		return
	end
	
	local meta
	if advtrains.is_node_loaded(pos) then
		meta=minetest.get_meta(pos)
	end
	
	if not nodetbl.env or not atlatc.envs[nodetbl.env] then
		atwarn("LuaATC component at",ph,": Not an existing environment: "..(nodetbl.env or "<nil>"))
		return false
	end
	local env = atlatc.envs[nodetbl.env]
	if not nodetbl.code or nodetbl.code=="" then
		if not ignore_no_code then
			env:log("warning", "LuaATC component at",ph,": No code to run! (insert -- to suppress warning)")
		end
		return false
	end
	
	local customfct=customfct_p or {}
	-- add interrupt function
	customfct.interrupt=function(t, imesg)
		assertt(t, "number")
		assert(t >= 0)
		atlatc.interrupt.add(t, pos, {type="int", int=true, message=imesg, msg=imesg}) --Compatiblity "message" field.
	end
	customfct.interrupt_safe=function(t, imesg)
		assertt(t, "number")
		assert(t >= 0)
		if atlatc.interrupt.has_at_pos(pos) then
			return false
		end
		atlatc.interrupt.add(t, pos, {type="int", int=true, message=imesg, msg=imesg}) --Compatiblity "message" field.
		return true
	end
	customfct.clear_interrupts=function()
		atlatc.interrupt.clear_ints_at_pos(pos)
	end
	customfct.atc_pos = {
		x = pos.x,
		y = pos.y,
		z = pos.z,
	}
	-- add digiline_send function, if digiline is loaded
	if minetest.global_exists("digiline") then
		customfct.digiline_send=function(channel, msg)
			assertt(channel, "string")
			if advtrains.is_node_loaded(pos) then
				digiline:receptor_send(pos, digiline.rules.default, channel, msg)
			end
		end
	end
	-- add lines scheduler if enabled
	if advtrains.lines and advtrains.lines.sched then
		customfct.schedule = function(rwtime, msg)
			return advtrains.lines.sched.enqueue(rwtime, "atlatc_env", {pos=pos, msg=msg}, advtrains.encode_pos(pos), 1)
		end
		customfct.schedule_in = function(rwtime, msg)
			return advtrains.lines.sched.enqueue_in(rwtime, "atlatc_env", {pos=pos, msg=msg}, advtrains.encode_pos(pos), 1)
		end
	end
	
	local datain=nodetbl.data or {}
	local succ, dataout = env:execute_code(datain, nodetbl.code, evtdata, customfct)
	if succ then
		atlatc.active.nodes[ph].data=atlatc.remove_invalid_data(dataout)
	else
		atlatc.active.nodes[ph].err=dataout
		env:log("error", "LuaATC component at",ph,": LUA Error:",dataout)
		if meta then
			meta:set_string("infotext", S("LuaATC component with error: @1", dataout))
		end
		--TODO temporary
		--if customfct.atc_id then
		--	advtrains.drb_dump(customfct.atc_id)
		--	error("Debug: LuaATC error hit!")
		--end
	end
	if meta then
		meta:set_string("formspec", ac.getform(pos, meta))
	end
end

function ac.on_digiline_receive(pos, node, channel, msg)
	atlatc.interrupt.add(0, pos, {type="digiline", digiline=true, channel = channel, msg = msg})
end

if advtrains.lines and advtrains.lines.sched then
	advtrains.lines.sched.register_callback("atlatc_env", function(data)
		-- This adds another interrupt to the atlatc queue... there might be a better way
		atlatc.interrupt.add(0, data.pos, {type="schedule",schedule=true, msg=data.msg})
	end)
end

ac.trackdef_advtrains_defs = {
	on_train_enter = function(pos, train_id)
		--do async. Event is fired in train steps
		atlatc.interrupt.add(0, pos, {type="train", train=true, id=train_id})
	end,
}

atlatc.active=ac