Line data Source code
1 : /*
2 : Minetest
3 : Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
4 :
5 : This program is free software; you can redistribute it and/or modify
6 : it under the terms of the GNU Lesser General Public License as published by
7 : the Free Software Foundation; either version 2.1 of the License, or
8 : (at your option) any later version.
9 :
10 : This program is distributed in the hope that it will be useful,
11 : but WITHOUT ANY WARRANTY; without even the implied warranty of
12 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 : GNU Lesser General Public License for more details.
14 :
15 : You should have received a copy of the GNU Lesser General Public License along
16 : with this program; if not, write to the Free Software Foundation, Inc.,
17 : 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 : */
19 :
20 : #include <iostream>
21 : #include <algorithm>
22 : #include <sstream>
23 : #include <IFileSystem.h>
24 : #include "jthread/jmutexautolock.h"
25 : #include "util/auth.h"
26 : #include "util/directiontables.h"
27 : #include "util/pointedthing.h"
28 : #include "util/serialize.h"
29 : #include "util/string.h"
30 : #include "util/srp.h"
31 : #include "client.h"
32 : #include "network/clientopcodes.h"
33 : #include "filesys.h"
34 : #include "porting.h"
35 : #include "mapblock_mesh.h"
36 : #include "mapblock.h"
37 : #include "minimap.h"
38 : #include "settings.h"
39 : #include "profiler.h"
40 : #include "gettext.h"
41 : #include "log.h"
42 : #include "nodemetadata.h"
43 : #include "itemdef.h"
44 : #include "shader.h"
45 : #include "clientmap.h"
46 : #include "clientmedia.h"
47 : #include "sound.h"
48 : #include "IMeshCache.h"
49 : #include "config.h"
50 : #include "version.h"
51 : #include "drawscene.h"
52 : #include "database-sqlite3.h"
53 : #include "serialization.h"
54 : #include "guiscalingfilter.h"
55 :
56 : extern gui::IGUIEnvironment* guienv;
57 :
58 : /*
59 : QueuedMeshUpdate
60 : */
61 :
62 2139 : QueuedMeshUpdate::QueuedMeshUpdate():
63 : p(-1337,-1337,-1337),
64 : data(NULL),
65 2139 : ack_block_to_server(false)
66 : {
67 2139 : }
68 :
69 4278 : QueuedMeshUpdate::~QueuedMeshUpdate()
70 : {
71 2139 : if(data)
72 2139 : delete data;
73 2139 : }
74 :
75 : /*
76 : MeshUpdateQueue
77 : */
78 :
79 1 : MeshUpdateQueue::MeshUpdateQueue()
80 : {
81 1 : }
82 :
83 2 : MeshUpdateQueue::~MeshUpdateQueue()
84 : {
85 2 : JMutexAutoLock lock(m_mutex);
86 :
87 2 : for(std::vector<QueuedMeshUpdate*>::iterator
88 1 : i = m_queue.begin();
89 2 : i != m_queue.end(); i++)
90 : {
91 0 : QueuedMeshUpdate *q = *i;
92 0 : delete q;
93 : }
94 1 : }
95 :
96 : /*
97 : peer_id=0 adds with nobody to send to
98 : */
99 3548 : void MeshUpdateQueue::addBlock(v3s16 p, MeshMakeData *data, bool ack_block_to_server, bool urgent)
100 : {
101 5687 : DSTACK(__FUNCTION_NAME);
102 :
103 : assert(data); // pre-condition
104 :
105 5687 : JMutexAutoLock lock(m_mutex);
106 :
107 3548 : if(urgent)
108 842 : m_urgents.insert(p);
109 :
110 : /*
111 : Find if block is already in queue.
112 : If it is, update the data and quit.
113 : */
114 199573 : for(std::vector<QueuedMeshUpdate*>::iterator
115 3548 : i = m_queue.begin();
116 135414 : i != m_queue.end(); i++)
117 : {
118 65568 : QueuedMeshUpdate *q = *i;
119 65568 : if(q->p == p)
120 : {
121 1409 : if(q->data)
122 1409 : delete q->data;
123 1409 : q->data = data;
124 1409 : if(ack_block_to_server)
125 20 : q->ack_block_to_server = true;
126 2818 : return;
127 : }
128 : }
129 :
130 : /*
131 : Add the block
132 : */
133 4278 : QueuedMeshUpdate *q = new QueuedMeshUpdate;
134 2139 : q->p = p;
135 2139 : q->data = data;
136 2139 : q->ack_block_to_server = ack_block_to_server;
137 2139 : m_queue.push_back(q);
138 : }
139 :
140 : // Returned pointer must be deleted
141 : // Returns NULL if queue is empty
142 2156 : QueuedMeshUpdate *MeshUpdateQueue::pop()
143 : {
144 4312 : JMutexAutoLock lock(m_mutex);
145 :
146 2156 : bool must_be_urgent = !m_urgents.empty();
147 21274 : for(std::vector<QueuedMeshUpdate*>::iterator
148 2156 : i = m_queue.begin();
149 15620 : i != m_queue.end(); i++)
150 : {
151 7793 : QueuedMeshUpdate *q = *i;
152 7793 : if(must_be_urgent && m_urgents.count(q->p) == 0)
153 5654 : continue;
154 2139 : m_queue.erase(i);
155 2139 : m_urgents.erase(q->p);
156 2139 : return q;
157 : }
158 17 : return NULL;
159 : }
160 :
161 : /*
162 : MeshUpdateThread
163 : */
164 :
165 3548 : void MeshUpdateThread::enqueueUpdate(v3s16 p, MeshMakeData *data,
166 : bool ack_block_to_server, bool urgent)
167 : {
168 3548 : m_queue_in.addBlock(p, data, ack_block_to_server, urgent);
169 3548 : deferUpdate();
170 3548 : }
171 :
172 2156 : void MeshUpdateThread::doUpdate()
173 : {
174 : QueuedMeshUpdate *q;
175 4295 : while ((q = m_queue_in.pop())) {
176 :
177 4278 : ScopeProfiler sp(g_profiler, "Client: Mesh making");
178 :
179 2139 : MapBlockMesh *mesh_new = new MapBlockMesh(q->data, m_camera_offset);
180 :
181 2139 : MeshUpdateResult r;
182 2139 : r.p = q->p;
183 2139 : r.mesh = mesh_new;
184 2139 : r.ack_block_to_server = q->ack_block_to_server;
185 :
186 2139 : m_queue_out.push_back(r);
187 :
188 2139 : delete q;
189 : }
190 17 : }
191 :
192 : /*
193 : Client
194 : */
195 :
196 1 : Client::Client(
197 : IrrlichtDevice *device,
198 : const char *playername,
199 : std::string password,
200 : MapDrawControl &control,
201 : IWritableTextureSource *tsrc,
202 : IWritableShaderSource *shsrc,
203 : IWritableItemDefManager *itemdef,
204 : IWritableNodeDefManager *nodedef,
205 : ISoundManager *sound,
206 : MtEventManager *event,
207 : bool ipv6
208 : ):
209 : m_packetcounter_timer(0.0),
210 : m_connection_reinit_timer(0.1),
211 : m_avg_rtt_timer(0.0),
212 : m_playerpos_send_timer(0.0),
213 : m_ignore_damage_timer(0.0),
214 : m_tsrc(tsrc),
215 : m_shsrc(shsrc),
216 : m_itemdef(itemdef),
217 : m_nodedef(nodedef),
218 : m_sound(sound),
219 : m_event(event),
220 : m_mesh_update_thread(),
221 : m_env(
222 : new ClientMap(this, this, control,
223 1 : device->getSceneManager()->getRootSceneNode(),
224 2 : device->getSceneManager(), 666),
225 1 : device->getSceneManager(),
226 : tsrc, this, device
227 : ),
228 : m_particle_manager(&m_env),
229 : m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, ipv6, this),
230 : m_device(device),
231 : m_server_ser_ver(SER_FMT_VER_INVALID),
232 : m_playeritem(0),
233 : m_inventory_updated(false),
234 : m_inventory_from_server(NULL),
235 : m_inventory_from_server_age(0.0),
236 : m_show_highlighted(false),
237 : m_animation_time(0),
238 : m_crack_level(-1),
239 : m_crack_pos(0,0,0),
240 : m_highlighted_pos(0,0,0),
241 : m_map_seed(0),
242 : m_password(password),
243 : m_chosen_auth_mech(AUTH_MECHANISM_NONE),
244 : m_auth_data(NULL),
245 : m_access_denied(false),
246 : m_itemdef_received(false),
247 : m_nodedef_received(false),
248 0 : m_media_downloader(new ClientMediaDownloader()),
249 : m_time_of_day_set(false),
250 : m_last_time_of_day_f(-1),
251 : m_time_of_day_update_timer(0),
252 : m_recommended_send_interval(0.1),
253 : m_removed_sounds_check_timer(0),
254 : m_state(LC_Created),
255 5 : m_localdb(NULL)
256 : {
257 : // Add local player
258 1 : m_env.addPlayer(new LocalPlayer(this, playername));
259 :
260 1 : m_mapper = new Mapper(device, this);
261 1 : m_cache_save_interval = g_settings->getU16("server_map_save_interval");
262 :
263 1 : m_cache_smooth_lighting = g_settings->getBool("smooth_lighting");
264 1 : m_cache_enable_shaders = g_settings->getBool("enable_shaders");
265 1 : }
266 :
267 1 : void Client::Stop()
268 : {
269 : //request all client managed threads to stop
270 1 : m_mesh_update_thread.Stop();
271 : // Save local server map
272 1 : if (m_localdb) {
273 0 : infostream << "Local map saving ended." << std::endl;
274 0 : m_localdb->endSave();
275 : }
276 1 : }
277 :
278 3 : bool Client::isShutdown()
279 : {
280 :
281 3 : if (!m_mesh_update_thread.IsRunning()) return true;
282 :
283 2 : return false;
284 : }
285 :
286 3 : Client::~Client()
287 : {
288 1 : m_con.Disconnect();
289 :
290 1 : m_mesh_update_thread.Stop();
291 1 : m_mesh_update_thread.Wait();
292 31 : while(!m_mesh_update_thread.m_queue_out.empty()) {
293 15 : MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_frontNoEx();
294 15 : delete r.mesh;
295 : }
296 :
297 :
298 1 : delete m_inventory_from_server;
299 :
300 : // Delete detached inventories
301 29 : for(std::map<std::string, Inventory*>::iterator
302 1 : i = m_detached_inventories.begin();
303 20 : i != m_detached_inventories.end(); i++){
304 9 : delete i->second;
305 : }
306 :
307 : // cleanup 3d model meshes on client shutdown
308 1 : while (m_device->getSceneManager()->getMeshCache()->getMeshCount() != 0) {
309 : scene::IAnimatedMesh * mesh =
310 0 : m_device->getSceneManager()->getMeshCache()->getMeshByIndex(0);
311 :
312 0 : if (mesh != NULL)
313 0 : m_device->getSceneManager()->getMeshCache()->removeMesh(mesh);
314 : }
315 2 : }
316 :
317 1 : void Client::connect(Address address,
318 : const std::string &address_name,
319 : bool is_local_server)
320 : {
321 2 : DSTACK(__FUNCTION_NAME);
322 :
323 1 : initLocalMapSaving(address, address_name, is_local_server);
324 :
325 1 : m_con.SetTimeoutMs(0);
326 1 : m_con.Connect(address);
327 1 : }
328 :
329 1193 : void Client::step(float dtime)
330 : {
331 2379 : DSTACK(__FUNCTION_NAME);
332 :
333 : // Limit a bit
334 1193 : if(dtime > 2.0)
335 1 : dtime = 2.0;
336 :
337 1193 : if(m_ignore_damage_timer > dtime)
338 169 : m_ignore_damage_timer -= dtime;
339 : else
340 1024 : m_ignore_damage_timer = 0.0;
341 :
342 1193 : m_animation_time += dtime;
343 1193 : if(m_animation_time > 60.0)
344 0 : m_animation_time -= 60.0;
345 :
346 1193 : m_time_of_day_update_timer += dtime;
347 :
348 1193 : ReceiveAll();
349 :
350 : /*
351 : Packet counter
352 : */
353 : {
354 1193 : float &counter = m_packetcounter_timer;
355 1193 : counter -= dtime;
356 1193 : if(counter <= 0.0)
357 : {
358 2 : counter = 20.0;
359 :
360 2 : infostream << "Client packetcounter (" << m_packetcounter_timer
361 2 : << "):"<<std::endl;
362 2 : m_packetcounter.print(infostream);
363 2 : m_packetcounter.clear();
364 : }
365 : }
366 :
367 : // UGLY hack to fix 2 second startup delay caused by non existent
368 : // server client startup synchronization in local server or singleplayer mode
369 : static bool initial_step = true;
370 1193 : if (initial_step) {
371 1 : initial_step = false;
372 : }
373 1192 : else if(m_state == LC_Created) {
374 7 : float &counter = m_connection_reinit_timer;
375 7 : counter -= dtime;
376 7 : if(counter <= 0.0) {
377 1 : counter = 2.0;
378 :
379 1 : Player *myplayer = m_env.getLocalPlayer();
380 1 : FATAL_ERROR_IF(myplayer == NULL, "Local player not found in environment.");
381 :
382 : // Send TOSERVER_INIT_LEGACY
383 : // [0] u16 TOSERVER_INIT_LEGACY
384 : // [2] u8 SER_FMT_VER_HIGHEST_READ
385 : // [3] u8[20] player_name
386 : // [23] u8[28] password (new in some version)
387 : // [51] u16 minimum supported network protocol version (added sometime)
388 : // [53] u16 maximum supported network protocol version (added later than the previous one)
389 :
390 : char pName[PLAYERNAME_SIZE];
391 : char pPassword[PASSWORD_SIZE];
392 1 : memset(pName, 0, PLAYERNAME_SIZE * sizeof(char));
393 1 : memset(pPassword, 0, PASSWORD_SIZE * sizeof(char));
394 :
395 2 : std::string hashed_password = translatePassword(myplayer->getName(), m_password);
396 1 : snprintf(pName, PLAYERNAME_SIZE, "%s", myplayer->getName());
397 1 : snprintf(pPassword, PASSWORD_SIZE, "%s", hashed_password.c_str());
398 :
399 1 : sendLegacyInit(pName, pPassword);
400 : if (LATEST_PROTOCOL_VERSION >= 25)
401 1 : sendInit(myplayer->getName());
402 : }
403 :
404 : // Not connected, return
405 7 : return;
406 : }
407 :
408 : /*
409 : Do stuff if connected
410 : */
411 :
412 : /*
413 : Run Map's timers and unload unused data
414 : */
415 1186 : const float map_timer_and_unload_dtime = 5.25;
416 1186 : if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime)) {
417 12 : ScopeProfiler sp(g_profiler, "Client: map timer and unload");
418 12 : std::vector<v3s16> deleted_blocks;
419 12 : m_env.getMap().timerUpdate(map_timer_and_unload_dtime,
420 : g_settings->getFloat("client_unload_unused_data_timeout"),
421 6 : &deleted_blocks);
422 :
423 : /*
424 : Send info to server
425 : NOTE: This loop is intentionally iterated the way it is.
426 : */
427 :
428 6 : std::vector<v3s16>::iterator i = deleted_blocks.begin();
429 12 : std::vector<v3s16> sendlist;
430 0 : for(;;) {
431 6 : if(sendlist.size() == 255 || i == deleted_blocks.end()) {
432 6 : if(sendlist.empty())
433 6 : break;
434 : /*
435 : [0] u16 command
436 : [2] u8 count
437 : [3] v3s16 pos_0
438 : [3+6] v3s16 pos_1
439 : ...
440 : */
441 :
442 0 : sendDeletedBlocks(sendlist);
443 :
444 0 : if(i == deleted_blocks.end())
445 0 : break;
446 :
447 0 : sendlist.clear();
448 : }
449 :
450 0 : sendlist.push_back(*i);
451 0 : ++i;
452 : }
453 : }
454 :
455 : /*
456 : Handle environment
457 : */
458 : // Control local player (0ms)
459 1186 : LocalPlayer *player = m_env.getLocalPlayer();
460 : assert(player != NULL);
461 1186 : player->applyControl(dtime);
462 :
463 : // Step environment
464 1186 : m_env.step(dtime);
465 :
466 : /*
467 : Get events
468 : */
469 0 : for(;;) {
470 1186 : ClientEnvEvent event = m_env.getClientEvent();
471 1186 : if(event.type == CEE_NONE) {
472 2372 : break;
473 : }
474 0 : else if(event.type == CEE_PLAYER_DAMAGE) {
475 0 : if(m_ignore_damage_timer <= 0) {
476 0 : u8 damage = event.player_damage.amount;
477 :
478 0 : if(event.player_damage.send_to_server)
479 0 : sendDamage(damage);
480 :
481 : // Add to ClientEvent queue
482 : ClientEvent event;
483 0 : event.type = CE_PLAYER_DAMAGE;
484 0 : event.player_damage.amount = damage;
485 0 : m_client_event_queue.push(event);
486 : }
487 : }
488 0 : else if(event.type == CEE_PLAYER_BREATH) {
489 0 : u16 breath = event.player_breath.amount;
490 0 : sendBreath(breath);
491 : }
492 : }
493 :
494 : /*
495 : Print some info
496 : */
497 1186 : float &counter = m_avg_rtt_timer;
498 1186 : counter += dtime;
499 1186 : if(counter >= 10) {
500 3 : counter = 0.0;
501 : // connectedAndInitialized() is true, peer exists.
502 3 : float avg_rtt = getRTT();
503 3 : infostream << "Client: avg_rtt=" << avg_rtt << std::endl;
504 : }
505 :
506 : /*
507 : Send player position to server
508 : */
509 : {
510 1186 : float &counter = m_playerpos_send_timer;
511 1186 : counter += dtime;
512 1186 : if((m_state == LC_Ready) && (counter >= m_recommended_send_interval))
513 : {
514 273 : counter = 0.0;
515 273 : sendPlayerPos();
516 : }
517 : }
518 :
519 : /*
520 : Replace updated meshes
521 : */
522 : {
523 1186 : int num_processed_meshes = 0;
524 5434 : while (!m_mesh_update_thread.m_queue_out.empty())
525 : {
526 2124 : num_processed_meshes++;
527 2124 : MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_frontNoEx();
528 2124 : MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(r.p);
529 2124 : MinimapMapblock *minimap_mapblock = NULL;
530 2124 : if (block) {
531 : // Delete the old mesh
532 2124 : if (block->mesh != NULL) {
533 1250 : delete block->mesh;
534 1250 : block->mesh = NULL;
535 : }
536 :
537 2124 : if (r.mesh)
538 2124 : minimap_mapblock = r.mesh->getMinimapMapblock();
539 :
540 2124 : if (r.mesh && r.mesh->getMesh()->getMeshBufferCount() == 0) {
541 220 : delete r.mesh;
542 220 : block->mesh = NULL;
543 : } else {
544 : // Replace with the new mesh
545 1904 : block->mesh = r.mesh;
546 : }
547 : } else {
548 0 : delete r.mesh;
549 0 : minimap_mapblock = NULL;
550 : }
551 :
552 2124 : m_mapper->addBlock(r.p, minimap_mapblock);
553 :
554 2124 : if (r.ack_block_to_server) {
555 : /*
556 : Acknowledge block
557 : [0] u8 count
558 : [1] v3s16 pos_0
559 : */
560 768 : sendGotBlocks(r.p);
561 : }
562 : }
563 :
564 1186 : if (num_processed_meshes > 0)
565 1040 : g_profiler->graphAdd("num_processed_meshes", num_processed_meshes);
566 : }
567 :
568 : /*
569 : Load fetched media
570 : */
571 1186 : if (m_media_downloader && m_media_downloader->isStarted()) {
572 1 : m_media_downloader->step(this);
573 1 : if (m_media_downloader->isDone()) {
574 1 : received_media();
575 1 : delete m_media_downloader;
576 1 : m_media_downloader = NULL;
577 : }
578 : }
579 :
580 : /*
581 : If the server didn't update the inventory in a while, revert
582 : the local inventory (so the player notices the lag problem
583 : and knows something is wrong).
584 : */
585 1186 : if(m_inventory_from_server)
586 : {
587 1166 : float interval = 10.0;
588 1166 : float count_before = floor(m_inventory_from_server_age / interval);
589 :
590 1166 : m_inventory_from_server_age += dtime;
591 :
592 1166 : float count_after = floor(m_inventory_from_server_age / interval);
593 :
594 1166 : if(count_after != count_before)
595 : {
596 : // Do this every <interval> seconds after TOCLIENT_INVENTORY
597 : // Reset the locally changed inventory to the authoritative inventory
598 3 : Player *player = m_env.getLocalPlayer();
599 3 : player->inventory = *m_inventory_from_server;
600 3 : m_inventory_updated = true;
601 : }
602 : }
603 :
604 : /*
605 : Update positions of sounds attached to objects
606 : */
607 : {
608 2549 : for(std::map<int, u16>::iterator
609 1186 : i = m_sounds_to_objects.begin();
610 2490 : i != m_sounds_to_objects.end(); i++)
611 : {
612 59 : int client_id = i->first;
613 59 : u16 object_id = i->second;
614 59 : ClientActiveObject *cao = m_env.getActiveObject(object_id);
615 59 : if(!cao)
616 0 : continue;
617 59 : v3f pos = cao->getPosition();
618 59 : m_sound->updateSoundPosition(client_id, pos);
619 : }
620 : }
621 :
622 : /*
623 : Handle removed remotely initiated sounds
624 : */
625 1186 : m_removed_sounds_check_timer += dtime;
626 1186 : if(m_removed_sounds_check_timer >= 2.32) {
627 14 : m_removed_sounds_check_timer = 0;
628 : // Find removed sounds and clear references to them
629 28 : std::vector<s32> removed_server_ids;
630 34 : for(std::map<s32, int>::iterator
631 14 : i = m_sounds_server_to_client.begin();
632 32 : i != m_sounds_server_to_client.end();) {
633 2 : s32 server_id = i->first;
634 2 : int client_id = i->second;
635 2 : i++;
636 2 : if(!m_sound->soundExists(client_id)) {
637 1 : m_sounds_server_to_client.erase(server_id);
638 1 : m_sounds_client_to_server.erase(client_id);
639 1 : m_sounds_to_objects.erase(client_id);
640 1 : removed_server_ids.push_back(server_id);
641 : }
642 : }
643 :
644 : // Sync to server
645 14 : if(!removed_server_ids.empty()) {
646 1 : sendRemovedSounds(removed_server_ids);
647 : }
648 : }
649 :
650 : // Write server map
651 1186 : if (m_localdb && m_localdb_save_interval.step(dtime,
652 0 : m_cache_save_interval)) {
653 0 : m_localdb->endSave();
654 0 : m_localdb->beginSave();
655 : }
656 : }
657 :
658 2635 : bool Client::loadMedia(const std::string &data, const std::string &filename)
659 : {
660 : // Silly irrlicht's const-incorrectness
661 5270 : Buffer<char> data_rw(data.c_str(), data.size());
662 :
663 5270 : std::string name;
664 :
665 : const char *image_ext[] = {
666 : ".png", ".jpg", ".bmp", ".tga",
667 : ".pcx", ".ppm", ".psd", ".wal", ".rgb",
668 : NULL
669 2635 : };
670 2635 : name = removeStringEnd(filename, image_ext);
671 2635 : if(name != "")
672 : {
673 2283 : verbosestream<<"Client: Attempting to load image "
674 2283 : <<"file \""<<filename<<"\""<<std::endl;
675 :
676 2283 : io::IFileSystem *irrfs = m_device->getFileSystem();
677 2283 : video::IVideoDriver *vdrv = m_device->getVideoDriver();
678 :
679 : // Create an irrlicht memory file
680 6849 : io::IReadFile *rfile = irrfs->createMemoryReadFile(
681 6849 : *data_rw, data_rw.getSize(), "_tempreadfile");
682 :
683 2283 : FATAL_ERROR_IF(!rfile, "Could not create irrlicht memory file.");
684 :
685 : // Read image
686 2283 : video::IImage *img = vdrv->createImageFromFile(rfile);
687 2283 : if(!img){
688 0 : errorstream<<"Client: Cannot create image from data of "
689 0 : <<"file \""<<filename<<"\""<<std::endl;
690 0 : rfile->drop();
691 0 : return false;
692 : }
693 : else {
694 2283 : m_tsrc->insertSourceImage(filename, img);
695 2283 : img->drop();
696 2283 : rfile->drop();
697 2283 : return true;
698 : }
699 : }
700 :
701 : const char *sound_ext[] = {
702 : ".0.ogg", ".1.ogg", ".2.ogg", ".3.ogg", ".4.ogg",
703 : ".5.ogg", ".6.ogg", ".7.ogg", ".8.ogg", ".9.ogg",
704 : ".ogg", NULL
705 352 : };
706 352 : name = removeStringEnd(filename, sound_ext);
707 352 : if(name != "")
708 : {
709 150 : verbosestream<<"Client: Attempting to load sound "
710 150 : <<"file \""<<filename<<"\""<<std::endl;
711 150 : m_sound->loadSoundData(name, data);
712 150 : return true;
713 : }
714 :
715 : const char *model_ext[] = {
716 : ".x", ".b3d", ".md2", ".obj",
717 : NULL
718 202 : };
719 202 : name = removeStringEnd(filename, model_ext);
720 202 : if(name != "")
721 : {
722 202 : verbosestream<<"Client: Storing model into memory: "
723 202 : <<"\""<<filename<<"\""<<std::endl;
724 202 : if(m_mesh_data.count(filename))
725 0 : errorstream<<"Multiple models with name \""<<filename.c_str()
726 0 : <<"\" found; replacing previous model"<<std::endl;
727 202 : m_mesh_data[filename] = data;
728 202 : return true;
729 : }
730 :
731 0 : errorstream<<"Client: Don't know how to load file \""
732 0 : <<filename<<"\""<<std::endl;
733 0 : return false;
734 : }
735 :
736 : // Virtual methods from con::PeerHandler
737 1 : void Client::peerAdded(con::Peer *peer)
738 : {
739 1 : infostream<<"Client::peerAdded(): peer->id="
740 2 : <<peer->id<<std::endl;
741 1 : }
742 0 : void Client::deletingPeer(con::Peer *peer, bool timeout)
743 : {
744 : infostream<<"Client::deletingPeer(): "
745 0 : "Server Peer is getting deleted "
746 0 : <<"(timeout="<<timeout<<")"<<std::endl;
747 0 : }
748 :
749 : /*
750 : u16 command
751 : u16 number of files requested
752 : for each file {
753 : u16 length of name
754 : string name
755 : }
756 : */
757 0 : void Client::request_media(const std::vector<std::string> &file_requests)
758 : {
759 0 : std::ostringstream os(std::ios_base::binary);
760 0 : writeU16(os, TOSERVER_REQUEST_MEDIA);
761 0 : size_t file_requests_size = file_requests.size();
762 :
763 0 : FATAL_ERROR_IF(file_requests_size > 0xFFFF, "Unsupported number of file requests");
764 :
765 : // Packet dynamicly resized
766 0 : NetworkPacket pkt(TOSERVER_REQUEST_MEDIA, 2 + 0);
767 :
768 0 : pkt << (u16) (file_requests_size & 0xFFFF);
769 :
770 0 : for(std::vector<std::string>::const_iterator i = file_requests.begin();
771 0 : i != file_requests.end(); ++i) {
772 0 : pkt << (*i);
773 : }
774 :
775 0 : Send(&pkt);
776 :
777 0 : infostream << "Client: Sending media request list to server ("
778 0 : << file_requests.size() << " files. packet size)" << std::endl;
779 0 : }
780 :
781 1 : void Client::received_media()
782 : {
783 2 : NetworkPacket pkt(TOSERVER_RECEIVED_MEDIA, 0);
784 1 : Send(&pkt);
785 1 : infostream << "Client: Notifying server that we received all media"
786 1 : << std::endl;
787 1 : }
788 :
789 1 : void Client::initLocalMapSaving(const Address &address,
790 : const std::string &hostname,
791 : bool is_local_server)
792 : {
793 1 : if (!g_settings->getBool("enable_local_map_saving") || is_local_server) {
794 1 : return;
795 : }
796 :
797 : const std::string world_path = porting::path_user
798 0 : + DIR_DELIM + "worlds"
799 0 : + DIR_DELIM + "server_"
800 0 : + hostname + "_" + to_string(address.getPort());
801 :
802 0 : fs::CreateAllDirs(world_path);
803 :
804 0 : m_localdb = new Database_SQLite3(world_path);
805 0 : m_localdb->beginSave();
806 0 : actionstream << "Local map saving started, map will be saved at '" << world_path << "'" << std::endl;
807 : }
808 :
809 1193 : void Client::ReceiveAll()
810 : {
811 2386 : DSTACK(__FUNCTION_NAME);
812 1193 : u32 start_ms = porting::getTimeMs();
813 1321 : for(;;)
814 : {
815 : // Limit time even if there would be huge amounts of data to
816 : // process
817 2514 : if(porting::getTimeMs() > start_ms + 100)
818 3 : break;
819 :
820 : try {
821 2511 : Receive();
822 1321 : g_profiler->graphAdd("client_received_packets", 1);
823 : }
824 2380 : catch(con::NoIncomingDataException &e) {
825 1190 : break;
826 : }
827 0 : catch(con::InvalidIncomingDataException &e) {
828 : infostream<<"Client::ReceiveAll(): "
829 0 : "InvalidIncomingDataException: what()="
830 0 : <<e.what()<<std::endl;
831 : }
832 : }
833 1193 : }
834 :
835 2511 : void Client::Receive()
836 : {
837 5022 : DSTACK(__FUNCTION_NAME);
838 5022 : NetworkPacket pkt;
839 2511 : m_con.Receive(&pkt);
840 1321 : ProcessData(&pkt);
841 1321 : }
842 :
843 1321 : inline void Client::handleCommand(NetworkPacket* pkt)
844 : {
845 1321 : const ToClientCommandHandler& opHandle = toClientCommandTable[pkt->getCommand()];
846 1321 : (this->*opHandle.handler)(pkt);
847 1321 : }
848 :
849 : /*
850 : sender_peer_id given to this shall be quaranteed to be a valid peer
851 : */
852 1321 : void Client::ProcessData(NetworkPacket *pkt)
853 : {
854 2641 : DSTACK(__FUNCTION_NAME);
855 :
856 1321 : ToClientCommand command = (ToClientCommand) pkt->getCommand();
857 1321 : u32 sender_peer_id = pkt->getPeerId();
858 :
859 : //infostream<<"Client: received command="<<command<<std::endl;
860 1321 : m_packetcounter.add((u16)command);
861 :
862 : /*
863 : If this check is removed, be sure to change the queue
864 : system to know the ids
865 : */
866 1321 : if(sender_peer_id != PEER_ID_SERVER) {
867 : infostream << "Client::ProcessData(): Discarding data not "
868 0 : "coming from server: peer_id=" << sender_peer_id
869 0 : << std::endl;
870 0 : return;
871 : }
872 :
873 : // Command must be handled into ToClientCommandHandler
874 1321 : if (command >= TOCLIENT_NUM_MSG_TYPES) {
875 0 : infostream << "Client: Ignoring unknown command "
876 0 : << command << std::endl;
877 0 : return;
878 : }
879 :
880 : /*
881 : * Those packets are handled before m_server_ser_ver is set, it's normal
882 : * But we must use the new ToClientConnectionState in the future,
883 : * as a byte mask
884 : */
885 1321 : if(toClientCommandTable[command].state == TOCLIENT_STATE_NOT_CONNECTED) {
886 1 : handleCommand(pkt);
887 1 : return;
888 : }
889 :
890 1320 : if(m_server_ser_ver == SER_FMT_VER_INVALID) {
891 : infostream << "Client: Server serialization"
892 : " format invalid or not initialized."
893 0 : " Skipping incoming command=" << command << std::endl;
894 0 : return;
895 : }
896 :
897 : /*
898 : Handle runtime commands
899 : */
900 :
901 1320 : handleCommand(pkt);
902 : }
903 :
904 991 : void Client::Send(NetworkPacket* pkt)
905 : {
906 1982 : m_con.Send(PEER_ID_SERVER,
907 991 : serverCommandFactoryTable[pkt->getCommand()].channel,
908 : pkt,
909 1982 : serverCommandFactoryTable[pkt->getCommand()].reliable);
910 991 : }
911 :
912 0 : void Client::interact(u8 action, const PointedThing& pointed)
913 : {
914 0 : if(m_state != LC_Ready) {
915 : errorstream << "Client::interact() "
916 0 : "Canceled (not connected)"
917 0 : << std::endl;
918 0 : return;
919 : }
920 :
921 : /*
922 : [0] u16 command
923 : [2] u8 action
924 : [3] u16 item
925 : [5] u32 length of the next item
926 : [9] serialized PointedThing
927 : actions:
928 : 0: start digging (from undersurface) or use
929 : 1: stop digging (all parameters ignored)
930 : 2: digging completed
931 : 3: place block or item (to abovesurface)
932 : 4: use item
933 : */
934 :
935 0 : NetworkPacket pkt(TOSERVER_INTERACT, 1 + 2 + 0);
936 :
937 0 : pkt << action;
938 0 : pkt << (u16)getPlayerItem();
939 :
940 0 : std::ostringstream tmp_os(std::ios::binary);
941 0 : pointed.serialize(tmp_os);
942 :
943 0 : pkt.putLongString(tmp_os.str());
944 :
945 0 : Send(&pkt);
946 : }
947 :
948 0 : void Client::deleteAuthData()
949 : {
950 0 : if (!m_auth_data)
951 0 : return;
952 :
953 0 : switch (m_chosen_auth_mech) {
954 : case AUTH_MECHANISM_FIRST_SRP:
955 0 : break;
956 : case AUTH_MECHANISM_SRP:
957 : case AUTH_MECHANISM_LEGACY_PASSWORD:
958 0 : srp_user_delete((SRPUser *) m_auth_data);
959 0 : m_auth_data = NULL;
960 0 : break;
961 : case AUTH_MECHANISM_NONE:
962 0 : break;
963 : }
964 : }
965 :
966 :
967 0 : AuthMechanism Client::choseAuthMech(const u32 mechs)
968 : {
969 0 : if (mechs & AUTH_MECHANISM_SRP)
970 0 : return AUTH_MECHANISM_SRP;
971 :
972 0 : if (mechs & AUTH_MECHANISM_FIRST_SRP)
973 0 : return AUTH_MECHANISM_FIRST_SRP;
974 :
975 0 : if (mechs & AUTH_MECHANISM_LEGACY_PASSWORD)
976 0 : return AUTH_MECHANISM_LEGACY_PASSWORD;
977 :
978 0 : return AUTH_MECHANISM_NONE;
979 : }
980 :
981 1 : void Client::sendLegacyInit(const char* playerName, const char* playerPassword)
982 : {
983 : NetworkPacket pkt(TOSERVER_INIT_LEGACY,
984 2 : 1 + PLAYERNAME_SIZE + PASSWORD_SIZE + 2 + 2);
985 :
986 1 : pkt << (u8) SER_FMT_VER_HIGHEST_READ;
987 1 : pkt.putRawString(playerName,PLAYERNAME_SIZE);
988 1 : pkt.putRawString(playerPassword, PASSWORD_SIZE);
989 1 : pkt << (u16) CLIENT_PROTOCOL_VERSION_MIN << (u16) CLIENT_PROTOCOL_VERSION_MAX;
990 :
991 1 : Send(&pkt);
992 1 : }
993 :
994 1 : void Client::sendInit(const std::string &playerName)
995 : {
996 2 : NetworkPacket pkt(TOSERVER_INIT, 1 + 2 + 2 + (1 + playerName.size()));
997 :
998 : // we don't support network compression yet
999 1 : u16 supp_comp_modes = NETPROTO_COMPRESSION_NONE;
1000 1 : pkt << (u8) SER_FMT_VER_HIGHEST_READ << (u16) supp_comp_modes;
1001 1 : pkt << (u16) CLIENT_PROTOCOL_VERSION_MIN << (u16) CLIENT_PROTOCOL_VERSION_MAX;
1002 1 : pkt << playerName;
1003 :
1004 1 : Send(&pkt);
1005 1 : }
1006 :
1007 0 : void Client::startAuth(AuthMechanism chosen_auth_mechanism)
1008 : {
1009 0 : m_chosen_auth_mech = chosen_auth_mechanism;
1010 :
1011 0 : switch (chosen_auth_mechanism) {
1012 : case AUTH_MECHANISM_FIRST_SRP: {
1013 : // send srp verifier to server
1014 0 : NetworkPacket resp_pkt(TOSERVER_FIRST_SRP, 0);
1015 : char *salt, *bytes_v;
1016 : std::size_t len_salt, len_v;
1017 0 : salt = NULL;
1018 0 : getSRPVerifier(getPlayerName(), m_password,
1019 0 : &salt, &len_salt, &bytes_v, &len_v);
1020 : resp_pkt
1021 0 : << std::string((char*)salt, len_salt)
1022 0 : << std::string((char*)bytes_v, len_v)
1023 0 : << (u8)((m_password == "") ? 1 : 0);
1024 0 : free(salt);
1025 0 : free(bytes_v);
1026 0 : Send(&resp_pkt);
1027 0 : break;
1028 : }
1029 : case AUTH_MECHANISM_SRP:
1030 : case AUTH_MECHANISM_LEGACY_PASSWORD: {
1031 0 : u8 based_on = 1;
1032 :
1033 0 : if (chosen_auth_mechanism == AUTH_MECHANISM_LEGACY_PASSWORD) {
1034 0 : m_password = translatePassword(getPlayerName(), m_password);
1035 0 : based_on = 0;
1036 : }
1037 :
1038 0 : std::string playername_u = lowercase(getPlayerName());
1039 0 : m_auth_data = srp_user_new(SRP_SHA256, SRP_NG_2048,
1040 0 : getPlayerName().c_str(), playername_u.c_str(),
1041 0 : (const unsigned char *) m_password.c_str(),
1042 0 : m_password.length(), NULL, NULL);
1043 0 : char *bytes_A = 0;
1044 0 : size_t len_A = 0;
1045 0 : srp_user_start_authentication((struct SRPUser *) m_auth_data,
1046 0 : NULL, NULL, 0, (unsigned char **) &bytes_A, &len_A);
1047 :
1048 0 : NetworkPacket resp_pkt(TOSERVER_SRP_BYTES_A, 0);
1049 0 : resp_pkt << std::string(bytes_A, len_A) << based_on;
1050 0 : free(bytes_A);
1051 0 : Send(&resp_pkt);
1052 0 : break;
1053 : }
1054 : case AUTH_MECHANISM_NONE:
1055 0 : break; // not handled in this method
1056 : }
1057 0 : }
1058 :
1059 0 : void Client::sendDeletedBlocks(std::vector<v3s16> &blocks)
1060 : {
1061 0 : NetworkPacket pkt(TOSERVER_DELETEDBLOCKS, 1 + sizeof(v3s16) * blocks.size());
1062 :
1063 0 : pkt << (u8) blocks.size();
1064 :
1065 0 : u32 k = 0;
1066 0 : for(std::vector<v3s16>::iterator
1067 0 : j = blocks.begin();
1068 0 : j != blocks.end(); ++j) {
1069 0 : pkt << *j;
1070 0 : k++;
1071 : }
1072 :
1073 0 : Send(&pkt);
1074 0 : }
1075 :
1076 768 : void Client::sendGotBlocks(v3s16 block)
1077 : {
1078 1536 : NetworkPacket pkt(TOSERVER_GOTBLOCKS, 1 + 6);
1079 768 : pkt << (u8) 1 << block;
1080 768 : Send(&pkt);
1081 768 : }
1082 :
1083 1 : void Client::sendRemovedSounds(std::vector<s32> &soundList)
1084 : {
1085 1 : size_t server_ids = soundList.size();
1086 : assert(server_ids <= 0xFFFF);
1087 :
1088 2 : NetworkPacket pkt(TOSERVER_REMOVED_SOUNDS, 2 + server_ids * 4);
1089 :
1090 1 : pkt << (u16) (server_ids & 0xFFFF);
1091 :
1092 6 : for(std::vector<s32>::iterator i = soundList.begin();
1093 4 : i != soundList.end(); i++)
1094 1 : pkt << *i;
1095 :
1096 1 : Send(&pkt);
1097 1 : }
1098 :
1099 0 : void Client::sendNodemetaFields(v3s16 p, const std::string &formname,
1100 : const StringMap &fields)
1101 : {
1102 0 : size_t fields_size = fields.size();
1103 :
1104 0 : FATAL_ERROR_IF(fields_size > 0xFFFF, "Unsupported number of nodemeta fields");
1105 :
1106 0 : NetworkPacket pkt(TOSERVER_NODEMETA_FIELDS, 0);
1107 :
1108 0 : pkt << p << formname << (u16) (fields_size & 0xFFFF);
1109 :
1110 0 : StringMap::const_iterator it;
1111 0 : for (it = fields.begin(); it != fields.end(); ++it) {
1112 0 : const std::string &name = it->first;
1113 0 : const std::string &value = it->second;
1114 0 : pkt << name;
1115 0 : pkt.putLongString(value);
1116 : }
1117 :
1118 0 : Send(&pkt);
1119 0 : }
1120 :
1121 0 : void Client::sendInventoryFields(const std::string &formname,
1122 : const StringMap &fields)
1123 : {
1124 0 : size_t fields_size = fields.size();
1125 0 : FATAL_ERROR_IF(fields_size > 0xFFFF, "Unsupported number of inventory fields");
1126 :
1127 0 : NetworkPacket pkt(TOSERVER_INVENTORY_FIELDS, 0);
1128 0 : pkt << formname << (u16) (fields_size & 0xFFFF);
1129 :
1130 0 : StringMap::const_iterator it;
1131 0 : for (it = fields.begin(); it != fields.end(); ++it) {
1132 0 : const std::string &name = it->first;
1133 0 : const std::string &value = it->second;
1134 0 : pkt << name;
1135 0 : pkt.putLongString(value);
1136 : }
1137 :
1138 0 : Send(&pkt);
1139 0 : }
1140 :
1141 0 : void Client::sendInventoryAction(InventoryAction *a)
1142 : {
1143 0 : std::ostringstream os(std::ios_base::binary);
1144 :
1145 0 : a->serialize(os);
1146 :
1147 : // Make data buffer
1148 0 : std::string s = os.str();
1149 :
1150 0 : NetworkPacket pkt(TOSERVER_INVENTORY_ACTION, s.size());
1151 0 : pkt.putRawString(s.c_str(),s.size());
1152 :
1153 0 : Send(&pkt);
1154 0 : }
1155 :
1156 0 : void Client::sendChatMessage(const std::wstring &message)
1157 : {
1158 0 : NetworkPacket pkt(TOSERVER_CHAT_MESSAGE, 2 + message.size() * sizeof(u16));
1159 :
1160 0 : pkt << message;
1161 :
1162 0 : Send(&pkt);
1163 0 : }
1164 :
1165 0 : void Client::sendChangePassword(const std::string &oldpassword,
1166 : const std::string &newpassword)
1167 : {
1168 0 : Player *player = m_env.getLocalPlayer();
1169 0 : if (player == NULL)
1170 0 : return;
1171 :
1172 0 : std::string playername = player->getName();
1173 0 : if (m_proto_ver >= 25) {
1174 : // get into sudo mode and then send new password to server
1175 0 : m_password = oldpassword;
1176 0 : m_new_password = newpassword;
1177 0 : startAuth(choseAuthMech(m_sudo_auth_methods));
1178 : } else {
1179 0 : std::string oldpwd = translatePassword(playername, oldpassword);
1180 0 : std::string newpwd = translatePassword(playername, newpassword);
1181 :
1182 0 : NetworkPacket pkt(TOSERVER_PASSWORD_LEGACY, 2 * PASSWORD_SIZE);
1183 :
1184 0 : for (u8 i = 0; i < PASSWORD_SIZE; i++) {
1185 0 : pkt << (u8) (i < oldpwd.length() ? oldpwd[i] : 0);
1186 : }
1187 :
1188 0 : for (u8 i = 0; i < PASSWORD_SIZE; i++) {
1189 0 : pkt << (u8) (i < newpwd.length() ? newpwd[i] : 0);
1190 : }
1191 0 : Send(&pkt);
1192 : }
1193 : }
1194 :
1195 :
1196 0 : void Client::sendDamage(u8 damage)
1197 : {
1198 0 : DSTACK(__FUNCTION_NAME);
1199 :
1200 0 : NetworkPacket pkt(TOSERVER_DAMAGE, sizeof(u8));
1201 0 : pkt << damage;
1202 0 : Send(&pkt);
1203 0 : }
1204 :
1205 0 : void Client::sendBreath(u16 breath)
1206 : {
1207 0 : DSTACK(__FUNCTION_NAME);
1208 :
1209 0 : NetworkPacket pkt(TOSERVER_BREATH, sizeof(u16));
1210 0 : pkt << breath;
1211 0 : Send(&pkt);
1212 0 : }
1213 :
1214 0 : void Client::sendRespawn()
1215 : {
1216 0 : DSTACK(__FUNCTION_NAME);
1217 :
1218 0 : NetworkPacket pkt(TOSERVER_RESPAWN, 0);
1219 0 : Send(&pkt);
1220 0 : }
1221 :
1222 1 : void Client::sendReady()
1223 : {
1224 2 : DSTACK(__FUNCTION_NAME);
1225 :
1226 : NetworkPacket pkt(TOSERVER_CLIENT_READY,
1227 2 : 1 + 1 + 1 + 1 + 2 + sizeof(char) * strlen(g_version_hash));
1228 :
1229 1 : pkt << (u8) VERSION_MAJOR << (u8) VERSION_MINOR << (u8) VERSION_PATCH
1230 2 : << (u8) 0 << (u16) strlen(g_version_hash);
1231 :
1232 1 : pkt.putRawString(g_version_hash, (u16) strlen(g_version_hash));
1233 1 : Send(&pkt);
1234 1 : }
1235 :
1236 273 : void Client::sendPlayerPos()
1237 : {
1238 273 : LocalPlayer *myplayer = m_env.getLocalPlayer();
1239 273 : if(myplayer == NULL)
1240 76 : return;
1241 :
1242 : // Save bandwidth by only updating position when something changed
1243 1344 : if(myplayer->last_position == myplayer->getPosition() &&
1244 651 : myplayer->last_speed == myplayer->getSpeed() &&
1245 206 : myplayer->last_pitch == myplayer->getPitch() &&
1246 975 : myplayer->last_yaw == myplayer->getYaw() &&
1247 76 : myplayer->last_keyPressed == myplayer->keyPressed)
1248 76 : return;
1249 :
1250 197 : myplayer->last_position = myplayer->getPosition();
1251 197 : myplayer->last_speed = myplayer->getSpeed();
1252 197 : myplayer->last_pitch = myplayer->getPitch();
1253 197 : myplayer->last_yaw = myplayer->getYaw();
1254 197 : myplayer->last_keyPressed = myplayer->keyPressed;
1255 :
1256 : u16 our_peer_id;
1257 : {
1258 : //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1259 197 : our_peer_id = m_con.GetPeerID();
1260 : }
1261 :
1262 : // Set peer id if not set already
1263 197 : if(myplayer->peer_id == PEER_ID_INEXISTENT)
1264 1 : myplayer->peer_id = our_peer_id;
1265 :
1266 : assert(myplayer->peer_id == our_peer_id);
1267 :
1268 197 : v3f pf = myplayer->getPosition();
1269 197 : v3f sf = myplayer->getSpeed();
1270 197 : s32 pitch = myplayer->getPitch() * 100;
1271 197 : s32 yaw = myplayer->getYaw() * 100;
1272 197 : u32 keyPressed = myplayer->keyPressed;
1273 :
1274 197 : v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
1275 197 : v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
1276 : /*
1277 : Format:
1278 : [0] v3s32 position*100
1279 : [12] v3s32 speed*100
1280 : [12+12] s32 pitch*100
1281 : [12+12+4] s32 yaw*100
1282 : [12+12+4+4] u32 keyPressed
1283 : */
1284 :
1285 394 : NetworkPacket pkt(TOSERVER_PLAYERPOS, 12 + 12 + 4 + 4 + 4);
1286 :
1287 197 : pkt << position << speed << pitch << yaw << keyPressed;
1288 :
1289 197 : Send(&pkt);
1290 : }
1291 :
1292 20 : void Client::sendPlayerItem(u16 item)
1293 : {
1294 20 : Player *myplayer = m_env.getLocalPlayer();
1295 20 : if(myplayer == NULL)
1296 0 : return;
1297 :
1298 20 : u16 our_peer_id = m_con.GetPeerID();
1299 :
1300 : // Set peer id if not set already
1301 20 : if(myplayer->peer_id == PEER_ID_INEXISTENT)
1302 0 : myplayer->peer_id = our_peer_id;
1303 : assert(myplayer->peer_id == our_peer_id);
1304 :
1305 40 : NetworkPacket pkt(TOSERVER_PLAYERITEM, 2);
1306 :
1307 20 : pkt << item;
1308 :
1309 20 : Send(&pkt);
1310 : }
1311 :
1312 6 : void Client::removeNode(v3s16 p)
1313 : {
1314 12 : std::map<v3s16, MapBlock*> modified_blocks;
1315 :
1316 : try {
1317 6 : m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
1318 : }
1319 0 : catch(InvalidPositionException &e) {
1320 : }
1321 :
1322 45 : for(std::map<v3s16, MapBlock *>::iterator
1323 6 : i = modified_blocks.begin();
1324 34 : i != modified_blocks.end(); ++i) {
1325 11 : addUpdateMeshTaskWithEdge(i->first, false, true);
1326 : }
1327 6 : }
1328 :
1329 49 : void Client::addNode(v3s16 p, MapNode n, bool remove_metadata)
1330 : {
1331 : //TimeTaker timer1("Client::addNode()");
1332 :
1333 98 : std::map<v3s16, MapBlock*> modified_blocks;
1334 :
1335 : try {
1336 : //TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
1337 49 : m_env.getMap().addNodeAndUpdate(p, n, modified_blocks, remove_metadata);
1338 : }
1339 0 : catch(InvalidPositionException &e) {
1340 : }
1341 :
1342 389 : for(std::map<v3s16, MapBlock *>::iterator
1343 49 : i = modified_blocks.begin();
1344 292 : i != modified_blocks.end(); ++i) {
1345 97 : addUpdateMeshTaskWithEdge(i->first, false, true);
1346 : }
1347 49 : }
1348 :
1349 1166 : void Client::setPlayerControl(PlayerControl &control)
1350 : {
1351 1166 : LocalPlayer *player = m_env.getLocalPlayer();
1352 : assert(player != NULL);
1353 1166 : player->control = control;
1354 1166 : }
1355 :
1356 20 : void Client::selectPlayerItem(u16 item)
1357 : {
1358 20 : m_playeritem = item;
1359 20 : m_inventory_updated = true;
1360 20 : sendPlayerItem(item);
1361 20 : }
1362 :
1363 : // Returns true if the inventory of the local player has been
1364 : // updated from the server. If it is true, it is set to false.
1365 1166 : bool Client::getLocalInventoryUpdated()
1366 : {
1367 1166 : bool updated = m_inventory_updated;
1368 1166 : m_inventory_updated = false;
1369 1166 : return updated;
1370 : }
1371 :
1372 : // Copies the inventory of the local player to parameter
1373 24 : void Client::getLocalInventory(Inventory &dst)
1374 : {
1375 24 : Player *player = m_env.getLocalPlayer();
1376 : assert(player != NULL);
1377 24 : dst = player->inventory;
1378 24 : }
1379 :
1380 0 : Inventory* Client::getInventory(const InventoryLocation &loc)
1381 : {
1382 0 : switch(loc.type){
1383 : case InventoryLocation::UNDEFINED:
1384 : {}
1385 0 : break;
1386 : case InventoryLocation::CURRENT_PLAYER:
1387 : {
1388 0 : Player *player = m_env.getLocalPlayer();
1389 : assert(player != NULL);
1390 0 : return &player->inventory;
1391 : }
1392 : break;
1393 : case InventoryLocation::PLAYER:
1394 : {
1395 0 : Player *player = m_env.getPlayer(loc.name.c_str());
1396 0 : if(!player)
1397 0 : return NULL;
1398 0 : return &player->inventory;
1399 : }
1400 : break;
1401 : case InventoryLocation::NODEMETA:
1402 : {
1403 0 : NodeMetadata *meta = m_env.getMap().getNodeMetadata(loc.p);
1404 0 : if(!meta)
1405 0 : return NULL;
1406 0 : return meta->getInventory();
1407 : }
1408 : break;
1409 : case InventoryLocation::DETACHED:
1410 : {
1411 0 : if(m_detached_inventories.count(loc.name) == 0)
1412 0 : return NULL;
1413 0 : return m_detached_inventories[loc.name];
1414 : }
1415 : break;
1416 : default:
1417 0 : FATAL_ERROR("Invalid inventory location type.");
1418 : break;
1419 : }
1420 0 : return NULL;
1421 : }
1422 :
1423 0 : void Client::inventoryAction(InventoryAction *a)
1424 : {
1425 : /*
1426 : Send it to the server
1427 : */
1428 0 : sendInventoryAction(a);
1429 :
1430 : /*
1431 : Predict some local inventory changes
1432 : */
1433 0 : a->clientApply(this, this);
1434 :
1435 : // Remove it
1436 0 : delete a;
1437 0 : }
1438 :
1439 1166 : ClientActiveObject * Client::getSelectedActiveObject(
1440 : f32 max_d,
1441 : v3f from_pos_f_on_map,
1442 : core::line3d<f32> shootline_on_map
1443 : )
1444 : {
1445 2332 : std::vector<DistanceSortedActiveObject> objects;
1446 :
1447 1166 : m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
1448 :
1449 : // Sort them.
1450 : // After this, the closest object is the first in the array.
1451 1166 : std::sort(objects.begin(), objects.end());
1452 :
1453 2511 : for(unsigned int i=0; i<objects.size(); i++)
1454 : {
1455 1345 : ClientActiveObject *obj = objects[i].obj;
1456 :
1457 1345 : core::aabbox3d<f32> *selection_box = obj->getSelectionBox();
1458 1345 : if(selection_box == NULL)
1459 1153 : continue;
1460 :
1461 192 : v3f pos = obj->getPosition();
1462 :
1463 : core::aabbox3d<f32> offsetted_box(
1464 384 : selection_box->MinEdge + pos,
1465 384 : selection_box->MaxEdge + pos
1466 192 : );
1467 :
1468 192 : if(offsetted_box.intersectsWithLine(shootline_on_map))
1469 : {
1470 0 : return obj;
1471 : }
1472 : }
1473 :
1474 1166 : return NULL;
1475 : }
1476 :
1477 0 : std::list<std::string> Client::getConnectedPlayerNames()
1478 : {
1479 0 : return m_env.getPlayerNames();
1480 : }
1481 :
1482 2332 : float Client::getAnimationTime()
1483 : {
1484 2332 : return m_animation_time;
1485 : }
1486 :
1487 2332 : int Client::getCrackLevel()
1488 : {
1489 2332 : return m_crack_level;
1490 : }
1491 :
1492 57 : void Client::setHighlighted(v3s16 pos, bool show_highlighted)
1493 : {
1494 57 : m_show_highlighted = show_highlighted;
1495 57 : v3s16 old_highlighted_pos = m_highlighted_pos;
1496 57 : m_highlighted_pos = pos;
1497 57 : addUpdateMeshTaskForNode(old_highlighted_pos, false, true);
1498 57 : addUpdateMeshTaskForNode(m_highlighted_pos, false, true);
1499 57 : }
1500 :
1501 0 : void Client::setCrack(int level, v3s16 pos)
1502 : {
1503 0 : int old_crack_level = m_crack_level;
1504 0 : v3s16 old_crack_pos = m_crack_pos;
1505 :
1506 0 : m_crack_level = level;
1507 0 : m_crack_pos = pos;
1508 :
1509 0 : if(old_crack_level >= 0 && (level < 0 || pos != old_crack_pos))
1510 : {
1511 : // remove old crack
1512 0 : addUpdateMeshTaskForNode(old_crack_pos, false, true);
1513 : }
1514 0 : if(level >= 0 && (old_crack_level < 0 || pos != old_crack_pos))
1515 : {
1516 : // add new crack
1517 0 : addUpdateMeshTaskForNode(pos, false, true);
1518 : }
1519 0 : }
1520 :
1521 0 : u16 Client::getHP()
1522 : {
1523 0 : Player *player = m_env.getLocalPlayer();
1524 : assert(player != NULL);
1525 0 : return player->hp;
1526 : }
1527 :
1528 0 : u16 Client::getBreath()
1529 : {
1530 0 : Player *player = m_env.getLocalPlayer();
1531 : assert(player != NULL);
1532 0 : return player->getBreath();
1533 : }
1534 :
1535 1168 : bool Client::getChatMessage(std::wstring &message)
1536 : {
1537 1168 : if(m_chat_queue.size() == 0)
1538 1166 : return false;
1539 2 : message = m_chat_queue.front();
1540 2 : m_chat_queue.pop();
1541 2 : return true;
1542 : }
1543 :
1544 0 : void Client::typeChatMessage(const std::wstring &message)
1545 : {
1546 : // Discard empty line
1547 0 : if(message == L"")
1548 0 : return;
1549 :
1550 : // Send to others
1551 0 : sendChatMessage(message);
1552 :
1553 : // Show locally
1554 0 : if (message[0] == L'/')
1555 : {
1556 0 : m_chat_queue.push((std::wstring)L"issued command: " + message);
1557 : }
1558 : else
1559 : {
1560 0 : LocalPlayer *player = m_env.getLocalPlayer();
1561 : assert(player != NULL);
1562 0 : std::wstring name = narrow_to_wide(player->getName());
1563 0 : m_chat_queue.push((std::wstring)L"<" + name + L"> " + message);
1564 : }
1565 : }
1566 :
1567 6504 : void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server, bool urgent)
1568 : {
1569 6504 : MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
1570 6504 : if(b == NULL)
1571 2956 : return;
1572 :
1573 : /*
1574 : Create a task to update the mesh of the block
1575 : */
1576 :
1577 3548 : MeshMakeData *data = new MeshMakeData(this, m_cache_enable_shaders);
1578 :
1579 : {
1580 : //TimeTaker timer("data fill");
1581 : // Release: ~0ms
1582 : // Debug: 1-6ms, avg=2ms
1583 3548 : data->fill(b);
1584 3548 : data->setCrack(m_crack_level, m_crack_pos);
1585 3548 : data->setHighlighted(m_highlighted_pos, m_show_highlighted);
1586 3548 : data->setSmoothLighting(m_cache_smooth_lighting);
1587 : }
1588 :
1589 : // Add task to queue
1590 3548 : m_mesh_update_thread.enqueueUpdate(p, data, ack_to_server, urgent);
1591 : }
1592 :
1593 894 : void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server, bool urgent)
1594 : {
1595 : try{
1596 894 : addUpdateMeshTask(blockpos, ack_to_server, urgent);
1597 : }
1598 0 : catch(InvalidPositionException &e){}
1599 :
1600 : // Leading edge
1601 6258 : for (int i=0;i<6;i++)
1602 : {
1603 : try{
1604 5364 : v3s16 p = blockpos + g_6dirs[i];
1605 5364 : addUpdateMeshTask(p, false, urgent);
1606 : }
1607 0 : catch(InvalidPositionException &e){}
1608 : }
1609 894 : }
1610 :
1611 114 : void Client::addUpdateMeshTaskForNode(v3s16 nodepos, bool ack_to_server, bool urgent)
1612 : {
1613 : {
1614 114 : v3s16 p = nodepos;
1615 114 : infostream<<"Client::addUpdateMeshTaskForNode(): "
1616 228 : <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
1617 114 : <<std::endl;
1618 : }
1619 :
1620 114 : v3s16 blockpos = getNodeBlockPos(nodepos);
1621 114 : v3s16 blockpos_relative = blockpos * MAP_BLOCKSIZE;
1622 :
1623 : try{
1624 114 : addUpdateMeshTask(blockpos, ack_to_server, urgent);
1625 : }
1626 0 : catch(InvalidPositionException &e) {}
1627 :
1628 : // Leading edge
1629 114 : if(nodepos.X == blockpos_relative.X){
1630 : try{
1631 30 : v3s16 p = blockpos + v3s16(-1,0,0);
1632 30 : addUpdateMeshTask(p, false, urgent);
1633 : }
1634 0 : catch(InvalidPositionException &e){}
1635 : }
1636 :
1637 114 : if(nodepos.Y == blockpos_relative.Y){
1638 : try{
1639 66 : v3s16 p = blockpos + v3s16(0,-1,0);
1640 66 : addUpdateMeshTask(p, false, urgent);
1641 : }
1642 0 : catch(InvalidPositionException &e){}
1643 : }
1644 :
1645 114 : if(nodepos.Z == blockpos_relative.Z){
1646 : try{
1647 36 : v3s16 p = blockpos + v3s16(0,0,-1);
1648 36 : addUpdateMeshTask(p, false, urgent);
1649 : }
1650 0 : catch(InvalidPositionException &e){}
1651 : }
1652 114 : }
1653 :
1654 1193 : ClientEvent Client::getClientEvent()
1655 : {
1656 : ClientEvent event;
1657 1193 : if(m_client_event_queue.size() == 0) {
1658 1166 : event.type = CE_NONE;
1659 : }
1660 : else {
1661 27 : event = m_client_event_queue.front();
1662 27 : m_client_event_queue.pop();
1663 : }
1664 1193 : return event;
1665 : }
1666 :
1667 1 : float Client::mediaReceiveProgress()
1668 : {
1669 1 : if (m_media_downloader)
1670 1 : return m_media_downloader->getProgress();
1671 : else
1672 0 : return 1.0; // downloader only exists when not yet done
1673 : }
1674 :
1675 : typedef struct TextureUpdateArgs {
1676 : IrrlichtDevice *device;
1677 : gui::IGUIEnvironment *guienv;
1678 : u32 last_time_ms;
1679 : u16 last_percent;
1680 : const wchar_t* text_base;
1681 : } TextureUpdateArgs;
1682 :
1683 5195 : void texture_update_progress(void *args, u32 progress, u32 max_progress)
1684 : {
1685 5195 : TextureUpdateArgs* targs = (TextureUpdateArgs*) args;
1686 5195 : u16 cur_percent = ceil(progress / (double) max_progress * 100.);
1687 :
1688 : // update the loading menu -- if neccessary
1689 5195 : bool do_draw = false;
1690 5195 : u32 time_ms = targs->last_time_ms;
1691 5195 : if (cur_percent != targs->last_percent) {
1692 100 : targs->last_percent = cur_percent;
1693 100 : time_ms = getTimeMs();
1694 : // only draw when the user will notice something:
1695 100 : do_draw = (time_ms - targs->last_time_ms > 100);
1696 : }
1697 :
1698 5195 : if (do_draw) {
1699 42 : targs->last_time_ms = time_ms;
1700 84 : std::basic_stringstream<wchar_t> strm;
1701 42 : strm << targs->text_base << " " << targs->last_percent << "%...";
1702 84 : draw_load_screen(strm.str(), targs->device, targs->guienv, 0,
1703 84 : 72 + (u16) ((18. / 100.) * (double) targs->last_percent));
1704 : }
1705 5195 : }
1706 :
1707 1 : void Client::afterContentReceived(IrrlichtDevice *device)
1708 : {
1709 1 : infostream<<"Client::afterContentReceived() started"<<std::endl;
1710 : assert(m_itemdef_received); // pre-condition
1711 : assert(m_nodedef_received); // pre-condition
1712 : assert(mediaReceived()); // pre-condition
1713 :
1714 1 : const wchar_t* text = wgettext("Loading textures...");
1715 :
1716 : // Clear cached pre-scaled 2D GUI images, as this cache
1717 : // might have images with the same name but different
1718 : // content from previous sessions.
1719 1 : guiScalingCacheClear(device->getVideoDriver());
1720 :
1721 : // Rebuild inherited images and recreate textures
1722 1 : infostream<<"- Rebuilding images and textures"<<std::endl;
1723 1 : draw_load_screen(text,device, guienv, 0, 70);
1724 1 : m_tsrc->rebuildImagesAndTextures();
1725 1 : delete[] text;
1726 :
1727 : // Rebuild shaders
1728 1 : infostream<<"- Rebuilding shaders"<<std::endl;
1729 1 : text = wgettext("Rebuilding shaders...");
1730 1 : draw_load_screen(text, device, guienv, 0, 71);
1731 1 : m_shsrc->rebuildShaders();
1732 1 : delete[] text;
1733 :
1734 : // Update node aliases
1735 1 : infostream<<"- Updating node aliases"<<std::endl;
1736 1 : text = wgettext("Initializing nodes...");
1737 1 : draw_load_screen(text, device, guienv, 0, 72);
1738 1 : m_nodedef->updateAliases(m_itemdef);
1739 2 : std::string texture_path = g_settings->get("texture_path");
1740 1 : if (texture_path != "" && fs::IsDir(texture_path))
1741 1 : m_nodedef->applyTextureOverrides(texture_path + DIR_DELIM + "override.txt");
1742 1 : m_nodedef->setNodeRegistrationStatus(true);
1743 1 : m_nodedef->runNodeResolveCallbacks();
1744 1 : delete[] text;
1745 :
1746 : // Update node textures and assign shaders to each tile
1747 1 : infostream<<"- Updating node textures"<<std::endl;
1748 : TextureUpdateArgs tu_args;
1749 1 : tu_args.device = device;
1750 1 : tu_args.guienv = guienv;
1751 1 : tu_args.last_time_ms = getTimeMs();
1752 1 : tu_args.last_percent = 0;
1753 1 : tu_args.text_base = wgettext("Initializing nodes");
1754 1 : m_nodedef->updateTextures(this, texture_update_progress, &tu_args);
1755 1 : delete[] tu_args.text_base;
1756 :
1757 : // Preload item textures and meshes if configured to
1758 1 : if(g_settings->getBool("preload_item_visuals"))
1759 : {
1760 0 : verbosestream<<"Updating item textures and meshes"<<std::endl;
1761 0 : text = wgettext("Item textures...");
1762 0 : draw_load_screen(text, device, guienv, 0, 0);
1763 0 : std::set<std::string> names = m_itemdef->getAll();
1764 0 : size_t size = names.size();
1765 0 : size_t count = 0;
1766 0 : int percent = 0;
1767 0 : for(std::set<std::string>::const_iterator
1768 0 : i = names.begin(); i != names.end(); ++i)
1769 : {
1770 : // Asking for these caches the result
1771 0 : m_itemdef->getInventoryTexture(*i, this);
1772 0 : m_itemdef->getWieldMesh(*i, this);
1773 0 : count++;
1774 0 : percent = (count * 100 / size * 0.2) + 80;
1775 0 : draw_load_screen(text, device, guienv, 0, percent);
1776 : }
1777 0 : delete[] text;
1778 : }
1779 :
1780 : // Start mesh update thread after setting up content definitions
1781 1 : infostream<<"- Starting mesh update thread"<<std::endl;
1782 1 : m_mesh_update_thread.Start();
1783 :
1784 1 : m_state = LC_Ready;
1785 1 : sendReady();
1786 1 : text = wgettext("Done!");
1787 1 : draw_load_screen(text, device, guienv, 0, 100);
1788 1 : infostream<<"Client::afterContentReceived() done"<<std::endl;
1789 1 : delete[] text;
1790 1 : }
1791 :
1792 3 : float Client::getRTT(void)
1793 : {
1794 3 : return m_con.getPeerStat(PEER_ID_SERVER,con::AVG_RTT);
1795 : }
1796 :
1797 0 : float Client::getCurRate(void)
1798 : {
1799 0 : return ( m_con.getLocalStat(con::CUR_INC_RATE) +
1800 0 : m_con.getLocalStat(con::CUR_DL_RATE));
1801 : }
1802 :
1803 0 : float Client::getAvgRate(void)
1804 : {
1805 0 : return ( m_con.getLocalStat(con::AVG_INC_RATE) +
1806 0 : m_con.getLocalStat(con::AVG_DL_RATE));
1807 : }
1808 :
1809 0 : void Client::makeScreenshot(IrrlichtDevice *device)
1810 : {
1811 0 : irr::video::IVideoDriver *driver = device->getVideoDriver();
1812 0 : irr::video::IImage* const raw_image = driver->createScreenShot();
1813 :
1814 0 : if (!raw_image)
1815 0 : return;
1816 :
1817 0 : time_t t = time(NULL);
1818 0 : struct tm *tm = localtime(&t);
1819 :
1820 : char timetstamp_c[64];
1821 0 : strftime(timetstamp_c, sizeof(timetstamp_c), "%Y%m%d_%H%M%S", tm);
1822 :
1823 0 : std::string filename_base = g_settings->get("screenshot_path")
1824 0 : + DIR_DELIM
1825 0 : + std::string("screenshot_")
1826 0 : + std::string(timetstamp_c);
1827 0 : std::string filename_ext = ".png";
1828 0 : std::string filename;
1829 :
1830 : // Try to find a unique filename
1831 0 : unsigned serial = 0;
1832 :
1833 0 : while (serial < SCREENSHOT_MAX_SERIAL_TRIES) {
1834 0 : filename = filename_base + (serial > 0 ? ("_" + itos(serial)) : "") + filename_ext;
1835 0 : std::ifstream tmp(filename.c_str());
1836 0 : if (!tmp.good())
1837 0 : break; // File did not apparently exist, we'll go with it
1838 0 : serial++;
1839 : }
1840 :
1841 0 : if (serial == SCREENSHOT_MAX_SERIAL_TRIES) {
1842 0 : infostream << "Could not find suitable filename for screenshot" << std::endl;
1843 : } else {
1844 : irr::video::IImage* const image =
1845 0 : driver->createImage(video::ECF_R8G8B8, raw_image->getDimension());
1846 :
1847 0 : if (image) {
1848 0 : raw_image->copyTo(image);
1849 :
1850 0 : std::ostringstream sstr;
1851 0 : if (driver->writeImageToFile(image, filename.c_str())) {
1852 0 : sstr << "Saved screenshot to '" << filename << "'";
1853 : } else {
1854 0 : sstr << "Failed to save screenshot '" << filename << "'";
1855 : }
1856 0 : m_chat_queue.push(narrow_to_wide(sstr.str()));
1857 0 : infostream << sstr.str() << std::endl;
1858 0 : image->drop();
1859 : }
1860 : }
1861 :
1862 0 : raw_image->drop();
1863 : }
1864 :
1865 : // IGameDef interface
1866 : // Under envlock
1867 203197 : IItemDefManager* Client::getItemDefManager()
1868 : {
1869 203197 : return m_itemdef;
1870 : }
1871 33142539 : INodeDefManager* Client::getNodeDefManager()
1872 : {
1873 33142539 : return m_nodedef;
1874 : }
1875 0 : ICraftDefManager* Client::getCraftDefManager()
1876 : {
1877 0 : return NULL;
1878 : //return m_craftdef;
1879 : }
1880 952278 : ITextureSource* Client::getTextureSource()
1881 : {
1882 952278 : return m_tsrc;
1883 : }
1884 2142 : IShaderSource* Client::getShaderSource()
1885 : {
1886 2142 : return m_shsrc;
1887 : }
1888 4280 : scene::ISceneManager* Client::getSceneManager()
1889 : {
1890 4280 : return m_device->getSceneManager();
1891 : }
1892 0 : u16 Client::allocateUnknownNodeId(const std::string &name)
1893 : {
1894 0 : errorstream << "Client::allocateUnknownNodeId(): "
1895 0 : << "Client cannot allocate node IDs" << std::endl;
1896 0 : FATAL_ERROR("Client allocated unknown node");
1897 :
1898 : return CONTENT_IGNORE;
1899 : }
1900 26 : ISoundManager* Client::getSoundManager()
1901 : {
1902 26 : return m_sound;
1903 : }
1904 38 : MtEventManager* Client::getEventManager()
1905 : {
1906 38 : return m_event;
1907 : }
1908 :
1909 1175 : ParticleManager* Client::getParticleManager()
1910 : {
1911 1175 : return &m_particle_manager;
1912 : }
1913 :
1914 1429 : scene::IAnimatedMesh* Client::getMesh(const std::string &filename)
1915 : {
1916 1429 : StringMap::const_iterator it = m_mesh_data.find(filename);
1917 1429 : if (it == m_mesh_data.end()) {
1918 0 : errorstream << "Client::getMesh(): Mesh not found: \"" << filename
1919 0 : << "\"" << std::endl;
1920 0 : return NULL;
1921 : }
1922 1429 : const std::string &data = it->second;
1923 1429 : scene::ISceneManager *smgr = m_device->getSceneManager();
1924 :
1925 : // Create the mesh, remove it from cache and return it
1926 : // This allows unique vertex colors and other properties for each instance
1927 2858 : Buffer<char> data_rw(data.c_str(), data.size()); // Const-incorrect Irrlicht
1928 1429 : io::IFileSystem *irrfs = m_device->getFileSystem();
1929 4287 : io::IReadFile *rfile = irrfs->createMemoryReadFile(
1930 4287 : *data_rw, data_rw.getSize(), filename.c_str());
1931 1429 : FATAL_ERROR_IF(!rfile, "Could not create/open RAM file");
1932 :
1933 1429 : scene::IAnimatedMesh *mesh = smgr->getMesh(rfile);
1934 1429 : rfile->drop();
1935 : // NOTE: By playing with Irrlicht refcounts, maybe we could cache a bunch
1936 : // of uniquely named instances and re-use them
1937 1429 : mesh->grab();
1938 1429 : smgr->getMeshCache()->removeMesh(mesh);
1939 1429 : return mesh;
1940 3 : }
|