1 /*
2 This file is part of Freeminer.
3
4 Freeminer is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
8
9 Freeminer is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with Freeminer. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 #include "circuit.h"
19 #include "circuit_element.h"
20 #include "debug.h"
21 #include "nodedef.h"
22 #include "mapblock.h"
23 #include "mapnode.h"
24 #include "scripting_game.h"
25 #include "map.h"
26 #include "serialization.h"
27 #include "settings.h"
28 #include "log.h"
29 #include "key_value_storage.h"
30 #include "filesys.h"
31
32 #include <map>
33 #include <iomanip>
34 #include <cassert>
35 #include <string>
36 #include <sstream>
37 #include <fstream>
38
39 #define PP(x) ((x).X)<<" "<<((x).Y)<<" "<<((x).Z)<<" "
40
41 const u32 Circuit::circuit_simulator_version = 1;
42 const char Circuit::elements_states_file[] = "circuit_elements_states";
43
Circuit(GameScripting * script,Map * map,INodeDefManager * ndef,std::string savedir)44 Circuit::Circuit(GameScripting* script, Map* map, INodeDefManager* ndef, std::string savedir) :
45 m_script(script),
46 m_map(map),
47 m_ndef(ndef),
48 m_min_update_delay(0.2f),
49 m_since_last_update(0.0f),
50 m_max_id(0),
51 m_max_virtual_id(1),
52 m_savedir(savedir)
53 {
54 load();
55 }
56
~Circuit()57 Circuit::~Circuit() {
58 save();
59 m_elements.clear();
60 delete m_database;
61 delete m_virtual_database;
62 m_script = nullptr;
63 m_map = nullptr;
64 m_ndef = nullptr;
65 m_database = nullptr;
66 m_virtual_database = nullptr;
67 }
68
open()69 void Circuit::open() {
70 m_database->open();
71 m_virtual_database->open();
72 }
73
close()74 void Circuit::close() {
75 m_database->close();
76 m_virtual_database->close();
77 }
78
addBlock(MapBlock * block)79 void Circuit::addBlock(MapBlock* block) {
80 // v3POS pos;
81 // for(pos.X = 0; pos.X < 16; ++pos.X)
82 // {
83 // for(pos.Y = 0; pos.Y < 16; ++pos.Y)
84 // {
85 // for(pos.Z = 0; pos.Z < 16; ++pos.Z)
86 // {
87 // MapNode tmp_node = block->getNode(pos);
88 // if(ndef->get(tmp_node).is_circuit_element)
89 // {
90 // pos.X = m_pos.X * MAP_BLOCKSIZE + x;
91 // pos.Y = m_pos.Y * MAP_BLOCKSIZE + y;
92 // pos.Z = m_pos.Z * MAP_BLOCKSIZE + z;
93 // }
94 // }
95 // }
96 // }
97 }
98
addNode(v3POS pos)99 void Circuit::addNode(v3POS pos) {
100 MapNode n = m_map->getNodeNoEx(pos);
101 const ContentFeatures& node_f = m_ndef->get(n);
102 if(node_f.is_wire || node_f.is_wire_connector) {
103 addWire(pos);
104 }
105 // Call circuit update
106 if(node_f.is_circuit_element) {
107 addElement(pos);
108 }
109 }
110
removeNode(v3POS pos,const MapNode & n_old)111 void Circuit::removeNode(v3POS pos, const MapNode& n_old) {
112 if(m_ndef->get(n_old).is_wire || m_ndef->get(n_old).is_wire_connector) {
113 removeWire(pos);
114 }
115 if(m_ndef->get(n_old).is_circuit_element) {
116 removeElement(pos);
117 }
118 }
119
swapNode(v3POS pos,const MapNode & n_old,const MapNode & n_new)120 void Circuit::swapNode(v3POS pos, const MapNode& n_old, const MapNode& n_new) {
121 const ContentFeatures& n_old_f = m_ndef->get(n_old);
122 const ContentFeatures& n_new_f = m_ndef->get(n_new);
123 if(n_new_f.is_circuit_element) {
124 if(n_old_f.is_circuit_element) {
125 swapElement(n_old, n_new, pos);
126 } else {
127 if(n_old_f.is_wire || n_old_f.is_wire_connector) {
128 removeWire(pos);
129 }
130 addElement(pos);
131 }
132 } else {
133 if(n_old_f.is_circuit_element) {
134 removeElement(pos);
135 } else if(n_old_f.is_wire || n_old_f.is_wire_connector) {
136 removeWire(pos);
137 }
138 if(n_new_f.is_wire) {
139 addWire(pos);
140 }
141 }
142 }
143
addElement(v3POS pos)144 void Circuit::addElement(v3POS pos) {
145 auto lock = m_elements_mutex.lock_unique_rec();
146
147 bool already_existed[6];
148 bool connected_faces[6] = {0};
149
150 std::vector <std::pair <std::list <CircuitElement>::iterator, u8> > connected;
151 MapNode node = m_map->getNodeNoEx(pos);
152
153 auto current_element_iterator = m_elements.insert(m_elements.begin(),
154 CircuitElement(pos, m_max_id++, m_ndef->get(node).circuit_element_delay));
155 m_pos_to_iterator[pos] = current_element_iterator;
156
157 // For each face add all other connected faces.
158 for(int i = 0; i < 6; ++i) {
159 if(!connected_faces[i]) {
160 connected.clear();
161 CircuitElement::findConnectedWithFace(connected, m_map, m_ndef, pos, SHIFT_TO_FACE(i), m_pos_to_iterator, connected_faces);
162 if(connected.size() > 0) {
163 std::list <CircuitElementVirtual>::iterator virtual_element_it;
164 bool found = false;
165 for(auto j = connected.begin(); j != connected.end(); ++j) {
166 if(j->first->getFace(j->second).is_connected) {
167 virtual_element_it = j->first->getFace(j->second).list_pointer;
168 found = true;
169 break;
170 }
171 }
172
173 // If virtual element already exist
174 if(found) {
175 already_existed[i] = true;
176 } else {
177 already_existed[i] = false;
178 virtual_element_it = m_virtual_elements.insert(m_virtual_elements.begin(),
179 CircuitElementVirtual(m_max_virtual_id++));
180 }
181
182 std::list <CircuitElementVirtualContainer>::iterator it;
183 for(auto j = connected.begin(); j != connected.end(); ++j) {
184 if(!j->first->getFace(j->second).is_connected) {
185 it = virtual_element_it->insert(virtual_element_it->begin(), CircuitElementVirtualContainer());
186 it->shift = j->second;
187 it->element_pointer = j->first;
188 j->first->connectFace(j->second, it, virtual_element_it);
189 }
190 }
191 it = virtual_element_it->insert(virtual_element_it->begin(), CircuitElementVirtualContainer());
192 it->shift = i;
193 it->element_pointer = current_element_iterator;
194 current_element_iterator->connectFace(i, it, virtual_element_it);
195 }
196
197 }
198 }
199
200 for(int i = 0; i < 6; ++i) {
201 if(current_element_iterator->getFace(i).is_connected && !already_existed[i]) {
202 saveVirtualElement(current_element_iterator->getFace(i).list_pointer, true);
203 }
204 }
205 saveElement(current_element_iterator, true);
206
207 }
208
removeElement(v3POS pos)209 void Circuit::removeElement(v3POS pos) {
210 auto lock = m_elements_mutex.lock_unique_rec();
211
212 std::vector <std::list <CircuitElementVirtual>::iterator> virtual_elements_for_update;
213 std::list <CircuitElement>::iterator current_element = m_pos_to_iterator[pos];
214 m_database->del(itos(current_element->getId()));
215
216 current_element->getNeighbors(virtual_elements_for_update);
217
218 m_elements.erase(current_element);
219
220 for(auto i = virtual_elements_for_update.begin(); i != virtual_elements_for_update.end(); ++i) {
221 if((*i)->size() > 1) {
222 std::ostringstream out(std::ios_base::binary);
223 (*i)->serialize(out);
224 m_virtual_database->put(itos((*i)->getId()), out.str());
225 } else {
226 m_virtual_database->del(itos((*i)->getId()));
227 std::list <CircuitElement>::iterator element_to_save;
228 for(auto j = (*i)->begin(); j != (*i)->end(); ++j) {
229 element_to_save = j->element_pointer;
230 }
231 m_virtual_elements.erase(*i);
232 saveElement(element_to_save, false);
233 }
234 }
235
236 m_pos_to_iterator.erase(pos);
237 }
238
addWire(v3POS pos)239 void Circuit::addWire(v3POS pos) {
240 auto lock = m_elements_mutex.lock_unique_rec();
241
242 // This is used for converting elements of current_face_connected to their ids in all_connected.
243 std::vector <std::pair <std::list <CircuitElement>::iterator, u8> > all_connected;
244 std::vector <std::list <CircuitElementVirtual>::iterator> created_virtual_elements;
245
246 bool used[6][6];
247 bool connected_faces[6];
248
249 MapNode node = m_map->getNode(pos);
250 std::vector <std::pair <std::list <CircuitElement>::iterator, u8> > connected_to_face[6];
251 for(int i = 0; i < 6; ++i) {
252 CircuitElement::findConnectedWithFace(connected_to_face[i], m_map, m_ndef, pos, SHIFT_TO_FACE(i),
253 m_pos_to_iterator, connected_faces);
254 }
255
256 for(int i = 0; i < 6; ++i) {
257 for(int j = 0; j < 6; ++j) {
258 used[i][j] = false;
259 }
260 }
261
262 // For each face connect faces, that are not yet connected.
263 for(int i = 0; i < 6; ++i) {
264 all_connected.clear();
265 u8 acceptable_faces = m_ndef->get(node).wire_connections[i];
266 for(int j = 0; j < 6; ++j) {
267 if((acceptable_faces & (SHIFT_TO_FACE(j))) && !used[i][j]) {
268 all_connected.insert(all_connected.end(), connected_to_face[j].begin(), connected_to_face[j].end());
269 used[i][j] = true;
270 used[j][i] = true;
271 }
272 }
273
274 if(all_connected.size() > 1) {
275 CircuitElementContainer element_with_virtual;
276 bool found_virtual = false;
277 for(auto i = all_connected.begin(); i != all_connected.end(); ++i) {
278 if(i->first->getFace(i->second).is_connected) {
279 element_with_virtual = i->first->getFace(i->second);
280 found_virtual = true;
281 break;
282 }
283 }
284
285 if(found_virtual) {
286 // Clear old connections (remove some virtual elements)
287 for(auto i = all_connected.begin(); i != all_connected.end(); ++i) {
288 if(i->first->getFace(i->second).is_connected
289 && (i->first->getFace(i->second).list_pointer != element_with_virtual.list_pointer)) {
290 m_virtual_database->del(itos(i->first->getFace(i->second).list_pointer->getId()));
291 i->first->disconnectFace(i->second);
292 m_virtual_elements.erase(i->first->getFace(i->second).list_pointer);
293 }
294 }
295 } else {
296 element_with_virtual.list_pointer = m_virtual_elements.insert(m_virtual_elements.begin(),
297 CircuitElementVirtual(m_max_virtual_id++));
298 }
299 created_virtual_elements.push_back(element_with_virtual.list_pointer);
300
301 // Create new connections
302 for(auto i = all_connected.begin(); i != all_connected.end(); ++i) {
303 if(!(i->first->getFace(i->second).is_connected)) {
304 auto it = element_with_virtual.list_pointer->insert(
305 element_with_virtual.list_pointer->begin(), CircuitElementVirtualContainer());
306 it->element_pointer = i->first;
307 it->shift = i->second;
308 i->first->connectFace(i->second, it, element_with_virtual.list_pointer);
309 }
310 }
311 }
312 }
313
314 for(u32 i = 0; i < created_virtual_elements.size(); ++i) {
315 saveVirtualElement(created_virtual_elements[i], true);
316 }
317 }
318
removeWire(v3POS pos)319 void Circuit::removeWire(v3POS pos) {
320 auto lock = m_elements_mutex.lock_unique_rec();
321
322 std::vector <std::pair <std::list <CircuitElement>::iterator, u8> > current_face_connected;
323
324 bool connected_faces[6];
325 for(int i = 0; i < 6; ++i) {
326 connected_faces[i] = false;
327 }
328
329 // Find and remove virtual elements
330 bool found_virtual_elements = false;
331 for(int i = 0; i < 6; ++i) {
332 if(!connected_faces[i]) {
333 current_face_connected.clear();
334 CircuitElement::findConnectedWithFace(current_face_connected, m_map, m_ndef, pos,
335 SHIFT_TO_FACE(i), m_pos_to_iterator, connected_faces);
336 for(auto j = current_face_connected.begin(); j != current_face_connected.end(); ++j) {
337 CircuitElementContainer current_edge = j->first->getFace(j->second);
338 if(current_edge.is_connected) {
339 found_virtual_elements = true;
340 m_virtual_database->del(itos(current_edge.list_pointer->getId()));
341 m_virtual_elements.erase(current_edge.list_pointer);
342 break;
343 }
344 }
345
346 for(auto j = current_face_connected.begin(); j != current_face_connected.end(); ++j) {
347 saveElement(j->first, false);
348 }
349 }
350 }
351
352 for(int i = 0; i < 6; ++i) {
353 connected_faces[i] = false;
354 }
355
356 if(found_virtual_elements) {
357 // Restore some previously deleted connections.
358 for(int i = 0; i < 6; ++i) {
359 if(!connected_faces[i]) {
360 current_face_connected.clear();
361 CircuitElement::findConnectedWithFace(current_face_connected, m_map, m_ndef, pos, SHIFT_TO_FACE(i),
362 m_pos_to_iterator, connected_faces);
363
364 if(current_face_connected.size() > 1) {
365 auto new_virtual_element = m_virtual_elements.insert(
366 m_virtual_elements.begin(), CircuitElementVirtual(m_max_virtual_id++));
367
368 for(u32 j = 0; j < current_face_connected.size(); ++j) {
369 auto new_container = new_virtual_element->insert(
370 new_virtual_element->begin(), CircuitElementVirtualContainer());
371 new_container->element_pointer = current_face_connected[j].first;
372 new_container->shift = current_face_connected[j].second;
373 current_face_connected[j].first->connectFace(current_face_connected[j].second,
374 new_container, new_virtual_element);
375
376 saveElement(current_face_connected[j].first, false);
377 }
378
379 saveVirtualElement(new_virtual_element, false);
380 }
381 }
382 }
383 }
384 }
385
update(float dtime)386 void Circuit::update(float dtime) {
387 if(m_since_last_update > m_min_update_delay) {
388 auto lock = m_elements_mutex.lock_unique_rec();
389 m_since_last_update -= m_min_update_delay;
390 // Each element send signal to other connected virtual elements.
391 bool is_map_loaded = true;
392 for(auto i = m_elements.begin(); i != m_elements.end(); ++i) {
393 i->update();
394 }
395
396 // Each virtual element send signal to other connected elements.
397 for(auto i = m_virtual_elements.begin(); i != m_virtual_elements.end(); ++i) {
398 i->update();
399 }
400
401 // Update state of each element.
402 for(auto i = m_elements.begin(); i != m_elements.end(); ++i) {
403 if(!i->updateState(m_script, m_map, m_ndef)) {
404 is_map_loaded = false;
405 break;
406 }
407 }
408 if(!is_map_loaded) {
409 for(auto i = m_elements.begin(); i != m_elements.end(); ++i) {
410 i->resetState();
411 }
412 }
413 } else {
414 m_since_last_update += dtime;
415 }
416 }
417
418
swapElement(const MapNode & n_old,const MapNode & n_new,v3POS pos)419 void Circuit::swapElement(const MapNode& n_old, const MapNode& n_new, v3POS pos) {
420 auto lock = m_elements_mutex.lock_unique_rec();
421
422 const ContentFeatures& n_old_features = m_ndef->get(n_old);
423 const ContentFeatures& n_new_features = m_ndef->get(n_new);
424 std::list <CircuitElement>::iterator current_element = m_pos_to_iterator[pos];
425 current_element->swap(n_old, n_old_features, n_new, n_new_features);
426 saveElement(current_element, false);
427
428 }
429
load()430 void Circuit::load() {
431 u32 element_id;
432 u32 version = 0;
433 std::istringstream in(std::ios_base::binary);
434
435 m_database = new KeyValueStorage(m_savedir, "circuit");
436 m_virtual_database = new KeyValueStorage(m_savedir, "circuit_virtual");
437
438 std::ifstream input_elements_states((m_savedir + DIR_DELIM + elements_states_file).c_str());
439
440 if(input_elements_states.good()) {
441 input_elements_states.read(reinterpret_cast<char*>(&version), sizeof(version));
442 }
443 #if USE_LEVELDB
444 // Filling list with empty virtual elements
445 auto virtual_it = m_virtual_database->new_iterator();
446 std::map <u32, std::list <CircuitElementVirtual>::iterator> id_to_virtual_element;
447 for(virtual_it->SeekToFirst(); virtual_it->Valid(); virtual_it->Next()) {
448 element_id = stoi(virtual_it->key().ToString());
449 id_to_virtual_element[element_id] =
450 m_virtual_elements.insert(m_virtual_elements.begin(), CircuitElementVirtual(element_id));
451 if(element_id + 1 > m_max_virtual_id) {
452 m_max_virtual_id = element_id + 1;
453 }
454 }
455
456 // Filling list with empty elements
457 auto it = m_database->new_iterator();
458 std::map <u32, std::list <CircuitElement>::iterator> id_to_element;
459 for(it->SeekToFirst(); it->Valid(); it->Next()) {
460 element_id = stoi(it->key().ToString());
461 id_to_element[element_id] =
462 m_elements.insert(m_elements.begin(), CircuitElement(element_id));
463 if(element_id + 1 > m_max_id) {
464 m_max_id = element_id + 1;
465 }
466 }
467
468 // Loading states of elements
469 if(input_elements_states.good()) {
470 for(u32 i = 0; i < m_elements.size(); ++i) {
471 input_elements_states.read(reinterpret_cast<char*>(&element_id), sizeof(element_id));
472 if(id_to_element.find(element_id) != id_to_element.end()) {
473 id_to_element[element_id]->deSerializeState(input_elements_states);
474 } else {
475 throw SerializationError(static_cast<std::string>("File \"")
476 + elements_states_file + "\" seems to be corrupted.");
477 }
478 }
479 }
480
481 // Loading elements data
482 for(it->SeekToFirst(); it->Valid(); it->Next()) {
483 in.str(it->value().ToString());
484 element_id = stoi(it->key().ToString());
485 auto current_element = id_to_element[element_id];
486 current_element->deSerialize(in, id_to_virtual_element);
487 m_pos_to_iterator[current_element->getPos()] = current_element;
488 }
489 delete it;
490
491 // Loading virtual elements data
492 for(virtual_it->SeekToFirst(); virtual_it->Valid(); virtual_it->Next()) {
493 in.str(virtual_it->value().ToString());
494 element_id = stoi(virtual_it->key().ToString());
495 auto current_element = id_to_virtual_element[element_id];
496 current_element->deSerialize(in, current_element, id_to_element);
497 }
498
499 delete virtual_it;
500 #endif
501 }
502
save()503 void Circuit::save() {
504 auto lock = m_elements_mutex.lock_shared_rec();
505 std::ostringstream ostr(std::ios_base::binary);
506 std::ofstream out((m_savedir + DIR_DELIM + elements_states_file).c_str(), std::ios_base::binary);
507 out.write(reinterpret_cast<const char*>(&circuit_simulator_version), sizeof(circuit_simulator_version));
508 for(std::list<CircuitElement>::iterator i = m_elements.begin(); i != m_elements.end(); ++i) {
509 i->serializeState(ostr);
510 }
511 out << ostr.str();
512 }
513
saveElement(std::list<CircuitElement>::iterator element,bool save_edges)514 inline void Circuit::saveElement(std::list<CircuitElement>::iterator element, bool save_edges) {
515 std::ostringstream out(std::ios_base::binary);
516 element->serialize(out);
517 m_database->put(itos(element->getId()), out.str());
518 if(save_edges) {
519 for(int i = 0; i < 6; ++i) {
520 CircuitElementContainer tmp_container = element->getFace(i);
521 if(tmp_container.is_connected) {
522 std::ostringstream out(std::ios_base::binary);
523 tmp_container.list_pointer->serialize(out);
524 m_virtual_database->put(itos(tmp_container.list_pointer->getId()), out.str());
525 }
526 }
527 }
528 }
529
saveVirtualElement(std::list<CircuitElementVirtual>::iterator element,bool save_edges)530 inline void Circuit::saveVirtualElement(std::list <CircuitElementVirtual>::iterator element, bool save_edges) {
531 std::ostringstream out(std::ios_base::binary);
532 element->serialize(out);
533 m_virtual_database->put(itos(element->getId()), out.str());
534 if(save_edges) {
535 for(std::list <CircuitElementVirtualContainer>::iterator i = element->begin(); i != element->end(); ++i) {
536 std::ostringstream out(std::ios_base::binary);
537 i->element_pointer->serialize(out);
538 m_database->put(itos(i->element_pointer->getId()), out.str());
539 }
540 }
541 }
542