3 Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
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 General Public License for more details.
15 You should have received a copy of the GNU 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.
20 #include "environment.h"
23 #include "collision.h"
24 #include "content_mapnode.h"
26 #include "serverobject.h"
27 #include "content_sao.h"
30 Environment::Environment():
35 Environment::~Environment()
38 for(core::list<Player*>::Iterator i = m_players.begin();
39 i != m_players.end(); i++)
45 void Environment::addPlayer(Player *player)
47 DSTACK(__FUNCTION_NAME);
49 Check that peer_ids are unique.
50 Also check that names are unique.
51 Exception: there can be multiple players with peer_id=0
53 // If peer id is non-zero, it has to be unique.
54 if(player->peer_id != 0)
55 assert(getPlayer(player->peer_id) == NULL);
56 // Name has to be unique.
57 assert(getPlayer(player->getName()) == NULL);
59 m_players.push_back(player);
62 void Environment::removePlayer(u16 peer_id)
64 DSTACK(__FUNCTION_NAME);
66 for(core::list<Player*>::Iterator i = m_players.begin();
67 i != m_players.end(); i++)
70 if(player->peer_id != peer_id)
75 // See if there is an another one
76 // (shouldn't be, but just to be sure)
81 Player * Environment::getPlayer(u16 peer_id)
83 for(core::list<Player*>::Iterator i = m_players.begin();
84 i != m_players.end(); i++)
87 if(player->peer_id == peer_id)
93 Player * Environment::getPlayer(const char *name)
95 for(core::list<Player*>::Iterator i = m_players.begin();
96 i != m_players.end(); i++)
99 if(strcmp(player->getName(), name) == 0)
105 Player * Environment::getRandomConnectedPlayer()
107 core::list<Player*> connected_players = getPlayers(true);
108 u32 chosen_one = myrand() % connected_players.size();
110 for(core::list<Player*>::Iterator
111 i = connected_players.begin();
112 i != connected_players.end(); i++)
124 Player * Environment::getNearestConnectedPlayer(v3f pos)
126 core::list<Player*> connected_players = getPlayers(true);
128 Player *nearest_player = NULL;
129 for(core::list<Player*>::Iterator
130 i = connected_players.begin();
131 i != connected_players.end(); i++)
134 f32 d = player->getPosition().getDistanceFrom(pos);
135 if(d < nearest_d || nearest_player == NULL)
138 nearest_player = player;
141 return nearest_player;
144 core::list<Player*> Environment::getPlayers()
149 core::list<Player*> Environment::getPlayers(bool ignore_disconnected)
151 core::list<Player*> newlist;
152 for(core::list<Player*>::Iterator
153 i = m_players.begin();
154 i != m_players.end(); i++)
158 if(ignore_disconnected)
160 // Ignore disconnected players
161 if(player->peer_id == 0)
165 newlist.push_back(player);
170 void Environment::printPlayers(std::ostream &o)
172 o<<"Players in environment:"<<std::endl;
173 for(core::list<Player*>::Iterator i = m_players.begin();
174 i != m_players.end(); i++)
177 o<<"Player peer_id="<<player->peer_id<<std::endl;
181 /*void Environment::setDayNightRatio(u32 r)
183 getDayNightRatio() = r;
186 u32 Environment::getDayNightRatio()
188 //return getDayNightRatio();
189 return time_to_daynight_ratio(m_time_of_day);
196 void fillRadiusBlock(v3s16 p0, s16 r, core::map<v3s16, bool> &list)
199 for(p.X=p0.X-r; p.X<=p0.X+r; p.X++)
200 for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++)
201 for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++)
208 void ActiveBlockList::update(core::list<v3s16> &active_positions,
210 core::map<v3s16, bool> &blocks_removed,
211 core::map<v3s16, bool> &blocks_added)
216 core::map<v3s16, bool> newlist;
217 for(core::list<v3s16>::Iterator i = active_positions.begin();
218 i != active_positions.end(); i++)
220 fillRadiusBlock(*i, radius, newlist);
224 Find out which blocks on the old list are not on the new list
226 // Go through old list
227 for(core::map<v3s16, bool>::Iterator i = m_list.getIterator();
228 i.atEnd()==false; i++)
230 v3s16 p = i.getNode()->getKey();
231 // If not on new list, it's been removed
232 if(newlist.find(p) == NULL)
233 blocks_removed.insert(p, true);
237 Find out which blocks on the new list are not on the old list
239 // Go through new list
240 for(core::map<v3s16, bool>::Iterator i = newlist.getIterator();
241 i.atEnd()==false; i++)
243 v3s16 p = i.getNode()->getKey();
244 // If not on old list, it's been added
245 if(m_list.find(p) == NULL)
246 blocks_added.insert(p, true);
253 for(core::map<v3s16, bool>::Iterator i = newlist.getIterator();
254 i.atEnd()==false; i++)
256 v3s16 p = i.getNode()->getKey();
257 m_list.insert(p, true);
265 ServerEnvironment::ServerEnvironment(ServerMap *map, Server *server):
268 m_random_spawn_timer(3),
269 m_send_recommended_timer(0),
271 m_game_time_fraction_counter(0)
275 ServerEnvironment::~ServerEnvironment()
277 // Clear active block list.
278 // This makes the next one delete all active objects.
279 m_active_blocks.clear();
281 // Convert all objects to static and delete the active objects
282 deactivateFarObjects(true);
288 void ServerEnvironment::serializePlayers(const std::string &savedir)
290 std::string players_path = savedir + "/players";
291 fs::CreateDir(players_path);
293 core::map<Player*, bool> saved_players;
295 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
296 for(u32 i=0; i<player_files.size(); i++)
298 if(player_files[i].dir)
301 // Full path to this file
302 std::string path = players_path + "/" + player_files[i].name;
304 //dstream<<"Checking player file "<<path<<std::endl;
306 // Load player to see what is its name
307 ServerRemotePlayer testplayer;
309 // Open file and deserialize
310 std::ifstream is(path.c_str(), std::ios_base::binary);
311 if(is.good() == false)
313 dstream<<"Failed to read "<<path<<std::endl;
316 testplayer.deSerialize(is);
319 //dstream<<"Loaded test player with name "<<testplayer.getName()<<std::endl;
321 // Search for the player
322 std::string playername = testplayer.getName();
323 Player *player = getPlayer(playername.c_str());
326 dstream<<"Didn't find matching player, ignoring file "<<path<<std::endl;
330 //dstream<<"Found matching player, overwriting."<<std::endl;
332 // OK, found. Save player there.
334 // Open file and serialize
335 std::ofstream os(path.c_str(), std::ios_base::binary);
336 if(os.good() == false)
338 dstream<<"Failed to overwrite "<<path<<std::endl;
341 player->serialize(os);
342 saved_players.insert(player, true);
346 for(core::list<Player*>::Iterator i = m_players.begin();
347 i != m_players.end(); i++)
350 if(saved_players.find(player) != NULL)
352 /*dstream<<"Player "<<player->getName()
353 <<" was already saved."<<std::endl;*/
356 std::string playername = player->getName();
357 // Don't save unnamed player
360 //dstream<<"Not saving unnamed player."<<std::endl;
366 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS) == false)
367 playername = "player";
368 std::string path = players_path + "/" + playername;
370 for(u32 i=0; i<1000; i++)
372 if(fs::PathExists(path) == false)
377 path = players_path + "/" + playername + itos(i);
381 dstream<<"WARNING: Didn't find free file for player"<<std::endl;
386 /*dstream<<"Saving player "<<player->getName()<<" to "
388 // Open file and serialize
389 std::ofstream os(path.c_str(), std::ios_base::binary);
390 if(os.good() == false)
392 dstream<<"WARNING: Failed to overwrite "<<path<<std::endl;
395 player->serialize(os);
396 saved_players.insert(player, true);
400 //dstream<<"Saved "<<saved_players.size()<<" players."<<std::endl;
403 void ServerEnvironment::deSerializePlayers(const std::string &savedir)
405 std::string players_path = savedir + "/players";
407 core::map<Player*, bool> saved_players;
409 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
410 for(u32 i=0; i<player_files.size(); i++)
412 if(player_files[i].dir)
415 // Full path to this file
416 std::string path = players_path + "/" + player_files[i].name;
418 dstream<<"Checking player file "<<path<<std::endl;
420 // Load player to see what is its name
421 ServerRemotePlayer testplayer;
423 // Open file and deserialize
424 std::ifstream is(path.c_str(), std::ios_base::binary);
425 if(is.good() == false)
427 dstream<<"Failed to read "<<path<<std::endl;
430 testplayer.deSerialize(is);
433 if(!string_allowed(testplayer.getName(), PLAYERNAME_ALLOWED_CHARS))
435 dstream<<"Not loading player with invalid name: "
436 <<testplayer.getName()<<std::endl;
439 dstream<<"Loaded test player with name "<<testplayer.getName()
442 // Search for the player
443 std::string playername = testplayer.getName();
444 Player *player = getPlayer(playername.c_str());
445 bool newplayer = false;
448 dstream<<"Is a new player"<<std::endl;
449 player = new ServerRemotePlayer();
455 dstream<<"Reading player "<<testplayer.getName()<<" from "
457 // Open file and deserialize
458 std::ifstream is(path.c_str(), std::ios_base::binary);
459 if(is.good() == false)
461 dstream<<"Failed to read "<<path<<std::endl;
464 player->deSerialize(is);
472 void ServerEnvironment::saveMeta(const std::string &savedir)
474 std::string path = savedir + "/env_meta.txt";
476 // Open file and serialize
477 std::ofstream os(path.c_str(), std::ios_base::binary);
478 if(os.good() == false)
480 dstream<<"WARNING: ServerEnvironment::saveMeta(): Failed to open "
482 throw SerializationError("Couldn't save env meta");
486 args.setU64("game_time", m_game_time);
487 args.setU64("time_of_day", getTimeOfDay());
492 void ServerEnvironment::loadMeta(const std::string &savedir)
494 std::string path = savedir + "/env_meta.txt";
496 // Open file and deserialize
497 std::ifstream is(path.c_str(), std::ios_base::binary);
498 if(is.good() == false)
500 dstream<<"WARNING: ServerEnvironment::loadMeta(): Failed to open "
502 throw SerializationError("Couldn't load env meta");
510 throw SerializationError
511 ("ServerEnvironment::loadMeta(): EnvArgsEnd not found");
513 std::getline(is, line);
514 std::string trimmedline = trim(line);
515 if(trimmedline == "EnvArgsEnd")
517 args.parseConfigLine(line);
521 m_game_time = args.getU64("game_time");
522 }catch(SettingNotFoundException &e){
523 // Getting this is crucial, otherwise timestamps are useless
524 throw SerializationError("Couldn't load env meta game_time");
528 m_time_of_day = args.getU64("time_of_day");
529 }catch(SettingNotFoundException &e){
530 // This is not as important
531 m_time_of_day = 9000;
536 // This is probably very useless
537 void spawnRandomObjects(MapBlock *block)
539 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
540 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
542 bool last_node_walkable = false;
543 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
546 MapNode n = block->getNodeNoEx(p);
547 if(n.getContent() == CONTENT_IGNORE)
549 if(content_features(n).liquid_type != LIQUID_NONE)
551 if(content_features(n).walkable)
553 last_node_walkable = true;
556 if(last_node_walkable)
558 // If block contains light information
559 if(content_features(n).param_type == CPT_LIGHT)
561 if(n.getLight(LIGHTBANK_DAY) <= 5)
563 if(myrand() % 1000 == 0)
565 v3f pos_f = intToFloat(p+block->getPosRelative(), BS);
567 ServerActiveObject *obj = new Oerkki1SAO(NULL,0,pos_f);
568 std::string data = obj->getStaticData();
569 StaticObject s_obj(obj->getType(),
570 obj->getBasePosition(), data);
572 block->m_static_objects.insert(0, s_obj);
574 block->setChangedFlag();
579 last_node_walkable = false;
585 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
587 // Get time difference
589 u32 stamp = block->getTimestamp();
590 if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
591 dtime_s = m_game_time - block->getTimestamp();
592 dtime_s += additional_dtime;
594 // Set current time as timestamp (and let it set ChangedFlag)
595 block->setTimestamp(m_game_time);
597 //dstream<<"Block is "<<dtime_s<<" seconds old."<<std::endl;
599 // Activate stored objects
600 activateObjects(block);
603 bool changed = block->m_node_metadata.step((float)dtime_s);
607 event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
608 event.p = block->getPos();
609 m_map->dispatchEvent(&event);
611 block->setChangedFlag();
614 // TODO: Do something
615 // TODO: Implement usage of ActiveBlockModifier
617 // Here's a quick demonstration
619 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
620 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
621 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
623 v3s16 p = p0 + block->getPosRelative();
624 MapNode n = block->getNodeNoEx(p0);
627 // Convert all mud under proper day lighting to grass
628 if(n.getContent() == CONTENT_MUD)
632 MapNode n_top = block->getNodeNoEx(p0+v3s16(0,1,0));
633 if(content_features(n_top).air_equivalent &&
634 n_top.getLight(LIGHTBANK_DAY) >= 13)
636 n.setContent(CONTENT_GRASS);
637 m_map->addNodeWithEvent(p, n);
645 void ServerEnvironment::step(float dtime)
647 DSTACK(__FUNCTION_NAME);
649 //TimeTaker timer("ServerEnv step");
652 bool footprints = g_settings.getBool("footprints");
658 m_game_time_fraction_counter += dtime;
659 u32 inc_i = (u32)m_game_time_fraction_counter;
660 m_game_time += inc_i;
661 m_game_time_fraction_counter -= (float)inc_i;
667 for(core::list<Player*>::Iterator i = m_players.begin();
668 i != m_players.end(); i++)
672 // Ignore disconnected players
673 if(player->peer_id == 0)
676 v3f playerpos = player->getPosition();
679 player->move(dtime, *m_map, 100*BS);
682 Add footsteps to grass
686 // Get node that is at BS/4 under player
687 v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
689 MapNode n = m_map->getNode(bottompos);
690 if(n.getContent() == CONTENT_GRASS)
692 n.setContent(CONTENT_GRASS_FOOTSTEPS);
693 m_map->setNode(bottompos, n);
696 catch(InvalidPositionException &e)
703 Manage active block list
705 if(m_active_blocks_management_interval.step(dtime, 2.0))
708 Get player block positions
710 core::list<v3s16> players_blockpos;
711 for(core::list<Player*>::Iterator
712 i = m_players.begin();
713 i != m_players.end(); i++)
716 // Ignore disconnected players
717 if(player->peer_id == 0)
719 v3s16 blockpos = getNodeBlockPos(
720 floatToInt(player->getPosition(), BS));
721 players_blockpos.push_back(blockpos);
725 Update list of active blocks, collecting changes
727 const s16 active_block_range = 5;
728 core::map<v3s16, bool> blocks_removed;
729 core::map<v3s16, bool> blocks_added;
730 m_active_blocks.update(players_blockpos, active_block_range,
731 blocks_removed, blocks_added);
734 Handle removed blocks
737 // Convert active objects that are no more in active blocks to static
738 deactivateFarObjects(false);
740 for(core::map<v3s16, bool>::Iterator
741 i = blocks_removed.getIterator();
742 i.atEnd()==false; i++)
744 v3s16 p = i.getNode()->getKey();
746 /*dstream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
747 <<") became inactive"<<std::endl;*/
749 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
753 // Set current time as timestamp (and let it set ChangedFlag)
754 block->setTimestamp(m_game_time);
761 for(core::map<v3s16, bool>::Iterator
762 i = blocks_added.getIterator();
763 i.atEnd()==false; i++)
765 v3s16 p = i.getNode()->getKey();
767 /*dstream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
768 <<") became active"<<std::endl;*/
770 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
774 activateBlock(block);
779 Mess around in active blocks
781 if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
785 for(core::map<v3s16, bool>::Iterator
786 i = m_active_blocks.m_list.getIterator();
787 i.atEnd()==false; i++)
789 v3s16 p = i.getNode()->getKey();
791 /*dstream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
792 <<") being handled"<<std::endl;*/
794 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
798 // Reset block usage timer
799 block->resetUsageTimer();
801 // Set current time as timestamp
802 block->setTimestampNoChangedFlag(m_game_time);
805 bool changed = block->m_node_metadata.step(dtime);
809 event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
811 m_map->dispatchEvent(&event);
813 block->setChangedFlag();
817 if(m_active_blocks_test_interval.step(dtime, 10.0))
819 //float dtime = 10.0;
821 for(core::map<v3s16, bool>::Iterator
822 i = m_active_blocks.m_list.getIterator();
823 i.atEnd()==false; i++)
825 v3s16 p = i.getNode()->getKey();
827 /*dstream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
828 <<") being handled"<<std::endl;*/
830 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
834 // Set current time as timestamp
835 block->setTimestampNoChangedFlag(m_game_time);
840 Note that map modifications should be done using the event-
841 making map methods so that the server gets information
844 Reading can be done quickly directly from the block.
846 Everything should bind to inside this single content
847 searching loop to keep things fast.
849 // TODO: Implement usage of ActiveBlockModifier
851 // Find out how many objects the block contains
852 u32 active_object_count = block->m_static_objects.m_active.size();
853 // Find out how many objects this and all the neighbors contain
854 u32 active_object_count_wider = 0;
855 for(s16 x=-1; x<=1; x++)
856 for(s16 y=-1; y<=1; y++)
857 for(s16 z=-1; z<=1; z++)
859 MapBlock *block = m_map->getBlockNoCreateNoEx(p+v3s16(x,y,z));
862 active_object_count_wider +=
863 block->m_static_objects.m_active.size();
867 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
868 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
869 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
871 v3s16 p = p0 + block->getPosRelative();
872 MapNode n = block->getNodeNoEx(p0);
876 Convert mud under proper lighting to grass
878 if(n.getContent() == CONTENT_MUD)
882 MapNode n_top = m_map->getNodeNoEx(p+v3s16(0,1,0));
883 if(content_features(n_top).air_equivalent &&
884 n_top.getLightBlend(getDayNightRatio()) >= 13)
886 n.setContent(CONTENT_GRASS);
887 m_map->addNodeWithEvent(p, n);
892 Convert grass into mud if under something else than air
894 if(n.getContent() == CONTENT_GRASS)
896 //if(myrand()%20 == 0)
898 MapNode n_top = m_map->getNodeNoEx(p+v3s16(0,1,0));
899 if(content_features(n_top).air_equivalent == false)
901 n.setContent(CONTENT_MUD);
902 m_map->addNodeWithEvent(p, n);
907 Rats spawn around regular trees
909 if(n.getContent() == CONTENT_TREE ||
910 n.getContent() == CONTENT_JUNGLETREE)
912 if(myrand()%200 == 0 && active_object_count_wider == 0)
914 v3s16 p1 = p + v3s16(myrand_range(-2, 2),
915 0, myrand_range(-2, 2));
916 MapNode n1 = m_map->getNodeNoEx(p1);
917 MapNode n1b = m_map->getNodeNoEx(p1+v3s16(0,-1,0));
918 if(n1b.getContent() == CONTENT_GRASS &&
919 n1.getContent() == CONTENT_AIR)
921 v3f pos = intToFloat(p1, BS);
922 ServerActiveObject *obj = new RatSAO(this, 0, pos);
923 addActiveObject(obj);
928 Make trees from saplings!
930 if(n.getContent() == CONTENT_SAPLING)
934 core::map<v3s16, MapBlock*> modified_blocks;
936 ManualMapVoxelManipulator vmanip(m_map);
937 v3s16 tree_blockp = getNodeBlockPos(tree_p);
938 vmanip.initialEmerge(tree_blockp - v3s16(1,1,1), tree_blockp + v3s16(1,1,1));
939 bool is_apple_tree = myrand()%4 == 0;
940 mapgen::make_tree(vmanip, tree_p, is_apple_tree);
941 vmanip.blitBackAll(&modified_blocks);
944 core::map<v3s16, MapBlock*> lighting_modified_blocks;
945 for(core::map<v3s16, MapBlock*>::Iterator
946 i = modified_blocks.getIterator();
947 i.atEnd() == false; i++)
949 lighting_modified_blocks.insert(i.getNode()->getKey(), i.getNode()->getValue());
951 m_map->updateLighting(lighting_modified_blocks, modified_blocks);
953 // Send a MEET_OTHER event
955 event.type = MEET_OTHER;
956 for(core::map<v3s16, MapBlock*>::Iterator
957 i = modified_blocks.getIterator();
958 i.atEnd() == false; i++)
960 v3s16 p = i.getNode()->getKey();
961 event.modified_blocks.insert(p, true);
963 m_map->dispatchEvent(&event);
975 //TimeTaker timer("Step active objects");
977 // This helps the objects to send data at the same time
978 bool send_recommended = false;
979 m_send_recommended_timer += dtime;
980 if(m_send_recommended_timer > 0.15)
982 m_send_recommended_timer = 0;
983 send_recommended = true;
986 for(core::map<u16, ServerActiveObject*>::Iterator
987 i = m_active_objects.getIterator();
988 i.atEnd()==false; i++)
990 ServerActiveObject* obj = i.getNode()->getValue();
991 // Don't step if is to be removed or stored statically
992 if(obj->m_removed || obj->m_pending_deactivation)
995 obj->step(dtime, send_recommended);
996 // Read messages from object
997 while(obj->m_messages_out.size() > 0)
999 m_active_object_messages.push_back(
1000 obj->m_messages_out.pop_front());
1006 Manage active objects
1008 if(m_object_management_interval.step(dtime, 0.5))
1011 Remove objects that satisfy (m_removed && m_known_by_count==0)
1013 removeRemovedObjects();
1016 if(g_settings.getBool("enable_experimental"))
1023 m_random_spawn_timer -= dtime;
1024 if(m_random_spawn_timer < 0)
1026 //m_random_spawn_timer += myrand_range(2.0, 20.0);
1027 //m_random_spawn_timer += 2.0;
1028 m_random_spawn_timer += 200.0;
1034 /*v2s16 p2d(myrand_range(-5,5), myrand_range(-5,5));
1035 s16 y = 1 + getServerMap().findGroundLevel(p2d);
1036 v3f pos(p2d.X*BS,y*BS,p2d.Y*BS);*/
1038 Player *player = getRandomConnectedPlayer();
1041 pos = player->getPosition();
1043 myrand_range(-3,3)*BS,
1045 myrand_range(-3,3)*BS
1049 Create a ServerActiveObject
1052 //TestSAO *obj = new TestSAO(this, 0, pos);
1053 //ServerActiveObject *obj = new ItemSAO(this, 0, pos, "CraftItem Stick 1");
1054 //ServerActiveObject *obj = new RatSAO(this, 0, pos);
1055 //ServerActiveObject *obj = new Oerkki1SAO(this, 0, pos);
1056 ServerActiveObject *obj = new FireflySAO(this, 0, pos);
1057 addActiveObject(obj);
1061 } // enable_experimental
1064 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1066 core::map<u16, ServerActiveObject*>::Node *n;
1067 n = m_active_objects.find(id);
1070 return n->getValue();
1073 bool isFreeServerActiveObjectId(u16 id,
1074 core::map<u16, ServerActiveObject*> &objects)
1079 for(core::map<u16, ServerActiveObject*>::Iterator
1080 i = objects.getIterator();
1081 i.atEnd()==false; i++)
1083 if(i.getNode()->getKey() == id)
1089 u16 getFreeServerActiveObjectId(
1090 core::map<u16, ServerActiveObject*> &objects)
1095 if(isFreeServerActiveObjectId(new_id, objects))
1105 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1108 u16 id = addActiveObjectRaw(object, true);
1113 Finds out what new objects have been added to
1114 inside a radius around a position
1116 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1117 core::map<u16, bool> ¤t_objects,
1118 core::map<u16, bool> &added_objects)
1120 v3f pos_f = intToFloat(pos, BS);
1121 f32 radius_f = radius * BS;
1123 Go through the object list,
1124 - discard m_removed objects,
1125 - discard objects that are too far away,
1126 - discard objects that are found in current_objects.
1127 - add remaining objects to added_objects
1129 for(core::map<u16, ServerActiveObject*>::Iterator
1130 i = m_active_objects.getIterator();
1131 i.atEnd()==false; i++)
1133 u16 id = i.getNode()->getKey();
1135 ServerActiveObject *object = i.getNode()->getValue();
1138 // Discard if removed
1139 if(object->m_removed)
1141 // Discard if too far
1142 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1143 if(distance_f > radius_f)
1145 // Discard if already on current_objects
1146 core::map<u16, bool>::Node *n;
1147 n = current_objects.find(id);
1150 // Add to added_objects
1151 added_objects.insert(id, false);
1156 Finds out what objects have been removed from
1157 inside a radius around a position
1159 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1160 core::map<u16, bool> ¤t_objects,
1161 core::map<u16, bool> &removed_objects)
1163 v3f pos_f = intToFloat(pos, BS);
1164 f32 radius_f = radius * BS;
1166 Go through current_objects; object is removed if:
1167 - object is not found in m_active_objects (this is actually an
1168 error condition; objects should be set m_removed=true and removed
1169 only after all clients have been informed about removal), or
1170 - object has m_removed=true, or
1171 - object is too far away
1173 for(core::map<u16, bool>::Iterator
1174 i = current_objects.getIterator();
1175 i.atEnd()==false; i++)
1177 u16 id = i.getNode()->getKey();
1178 ServerActiveObject *object = getActiveObject(id);
1181 dstream<<"WARNING: ServerEnvironment::getRemovedActiveObjects():"
1182 <<" object in current_objects is NULL"<<std::endl;
1184 else if(object->m_removed == false)
1186 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1187 /*dstream<<"removed == false"
1188 <<"distance_f = "<<distance_f
1189 <<", radius_f = "<<radius_f<<std::endl;*/
1190 if(distance_f < radius_f)
1196 removed_objects.insert(id, false);
1200 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1202 if(m_active_object_messages.size() == 0)
1203 return ActiveObjectMessage(0);
1205 return m_active_object_messages.pop_front();
1209 ************ Private methods *************
1212 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1216 if(object->getId() == 0)
1218 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1221 dstream<<"WARNING: ServerEnvironment::addActiveObjectRaw(): "
1222 <<"no free ids available"<<std::endl;
1226 object->setId(new_id);
1228 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1230 dstream<<"WARNING: ServerEnvironment::addActiveObjectRaw(): "
1231 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1235 /*dstream<<"INFO: ServerEnvironment::addActiveObjectRaw(): "
1236 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1238 m_active_objects.insert(object->getId(), object);
1240 // Add static object to active static list of the block
1241 v3f objectpos = object->getBasePosition();
1242 std::string staticdata = object->getStaticData();
1243 StaticObject s_obj(object->getType(), objectpos, staticdata);
1244 // Add to the block where the object is located in
1245 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1246 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1249 block->m_static_objects.m_active.insert(object->getId(), s_obj);
1250 object->m_static_exists = true;
1251 object->m_static_block = blockpos;
1254 block->setChangedFlag();
1257 dstream<<"WARNING: ServerEnv: Could not find a block for "
1258 <<"storing newly added static active object"<<std::endl;
1261 return object->getId();
1265 Remove objects that satisfy (m_removed && m_known_by_count==0)
1267 void ServerEnvironment::removeRemovedObjects()
1269 core::list<u16> objects_to_remove;
1270 for(core::map<u16, ServerActiveObject*>::Iterator
1271 i = m_active_objects.getIterator();
1272 i.atEnd()==false; i++)
1274 u16 id = i.getNode()->getKey();
1275 ServerActiveObject* obj = i.getNode()->getValue();
1276 // This shouldn't happen but check it
1279 dstream<<"WARNING: NULL object found in ServerEnvironment"
1280 <<" while finding removed objects. id="<<id<<std::endl;
1281 // Id to be removed from m_active_objects
1282 objects_to_remove.push_back(id);
1287 We will delete objects that are marked as removed or thatare
1288 waiting for deletion after deactivation
1290 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1294 Delete static data from block if is marked as removed
1296 if(obj->m_static_exists && obj->m_removed)
1298 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
1301 block->m_static_objects.remove(id);
1302 block->setChangedFlag();
1306 // If m_known_by_count > 0, don't actually remove.
1307 if(obj->m_known_by_count > 0)
1312 // Id to be removed from m_active_objects
1313 objects_to_remove.push_back(id);
1315 // Remove references from m_active_objects
1316 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1317 i != objects_to_remove.end(); i++)
1319 m_active_objects.remove(*i);
1324 Convert stored objects from blocks near the players to active.
1326 void ServerEnvironment::activateObjects(MapBlock *block)
1330 // Ignore if no stored objects (to not set changed flag)
1331 if(block->m_static_objects.m_stored.size() == 0)
1333 // A list for objects that couldn't be converted to static for some
1334 // reason. They will be stored back.
1335 core::list<StaticObject> new_stored;
1336 // Loop through stored static objects
1337 for(core::list<StaticObject>::Iterator
1338 i = block->m_static_objects.m_stored.begin();
1339 i != block->m_static_objects.m_stored.end(); i++)
1341 /*dstream<<"INFO: Server: Creating an active object from "
1342 <<"static data"<<std::endl;*/
1343 StaticObject &s_obj = *i;
1344 // Create an active object from the data
1345 ServerActiveObject *obj = ServerActiveObject::create
1346 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1347 // If couldn't create object, store static data back.
1350 new_stored.push_back(s_obj);
1353 // This will also add the object to the active static list
1354 addActiveObjectRaw(obj, false);
1355 //u16 id = addActiveObjectRaw(obj, false);
1357 // Clear stored list
1358 block->m_static_objects.m_stored.clear();
1359 // Add leftover failed stuff to stored list
1360 for(core::list<StaticObject>::Iterator
1361 i = new_stored.begin();
1362 i != new_stored.end(); i++)
1364 StaticObject &s_obj = *i;
1365 block->m_static_objects.m_stored.push_back(s_obj);
1367 // Block has been modified
1368 // NOTE: No it has not really. Save I/O here.
1369 //block->setChangedFlag();
1373 Convert objects that are not in active blocks to static.
1375 If m_known_by_count != 0, active object is not deleted, but static
1376 data is still updated.
1378 If force_delete is set, active object is deleted nevertheless. It
1379 shall only be set so in the destructor of the environment.
1381 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1383 core::list<u16> objects_to_remove;
1384 for(core::map<u16, ServerActiveObject*>::Iterator
1385 i = m_active_objects.getIterator();
1386 i.atEnd()==false; i++)
1388 ServerActiveObject* obj = i.getNode()->getValue();
1389 u16 id = i.getNode()->getKey();
1390 v3f objectpos = obj->getBasePosition();
1392 // This shouldn't happen but check it
1395 dstream<<"WARNING: NULL object found in ServerEnvironment"
1401 // The block in which the object resides in
1402 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1404 // If block is active, don't remove
1405 if(m_active_blocks.contains(blockpos_o))
1409 Update the static data
1412 // Delete old static object
1413 MapBlock *oldblock = NULL;
1414 if(obj->m_static_exists)
1416 MapBlock *block = m_map->getBlockNoCreateNoEx
1417 (obj->m_static_block);
1420 block->m_static_objects.remove(id);
1424 // Create new static object
1425 std::string staticdata = obj->getStaticData();
1426 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1427 // Add to the block where the object is located in
1428 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1429 // Get or generate the block
1430 MapBlock *block = m_map->emergeBlock(blockpos);
1432 /*MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1435 // Block not found. Is the old block still ok?
1438 // Load from disk or generate
1440 block = m_map->emergeBlock(blockpos);
1445 block->m_static_objects.insert(0, s_obj);
1446 block->setChangedFlag();
1447 obj->m_static_exists = true;
1448 obj->m_static_block = block->getPos();
1451 dstream<<"WARNING: ServerEnv: Could not find or generate "
1452 <<"a block for storing static object"<<std::endl;
1453 obj->m_static_exists = false;
1458 Delete active object if not known by some client,
1459 else set pending deactivation
1462 // If known by some client, don't delete.
1463 if(obj->m_known_by_count > 0 && force_delete == false)
1465 obj->m_pending_deactivation = true;
1469 /*dstream<<"INFO: Server: Stored static data. Deleting object."
1471 // Delete active object
1473 // Id to be removed from m_active_objects
1474 objects_to_remove.push_back(id);
1477 // Remove references from m_active_objects
1478 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1479 i != objects_to_remove.end(); i++)
1481 m_active_objects.remove(*i);
1492 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr):
1500 ClientEnvironment::~ClientEnvironment()
1502 // delete active objects
1503 for(core::map<u16, ClientActiveObject*>::Iterator
1504 i = m_active_objects.getIterator();
1505 i.atEnd()==false; i++)
1507 delete i.getNode()->getValue();
1514 void ClientEnvironment::addPlayer(Player *player)
1516 DSTACK(__FUNCTION_NAME);
1518 It is a failure if player is local and there already is a local
1521 assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
1523 Environment::addPlayer(player);
1526 LocalPlayer * ClientEnvironment::getLocalPlayer()
1528 for(core::list<Player*>::Iterator i = m_players.begin();
1529 i != m_players.end(); i++)
1531 Player *player = *i;
1532 if(player->isLocal())
1533 return (LocalPlayer*)player;
1538 void ClientEnvironment::step(float dtime)
1540 DSTACK(__FUNCTION_NAME);
1542 // Get some settings
1543 bool free_move = g_settings.getBool("free_move");
1544 bool footprints = g_settings.getBool("footprints");
1547 LocalPlayer *lplayer = getLocalPlayer();
1549 // collision info queue
1550 core::list<CollisionInfo> player_collisions;
1553 Get the speed the player is going
1555 bool is_climbing = lplayer->is_climbing;
1558 Check if the player is frozen (don't apply physics)
1560 bool is_frozen = lplayer->is_frozen;
1562 f32 player_speed = 0.001; // just some small value
1563 player_speed = lplayer->getSpeed().getLength();
1566 Maximum position increment
1568 //f32 position_max_increment = 0.05*BS;
1569 f32 position_max_increment = 0.1*BS;
1571 // Maximum time increment (for collision detection etc)
1572 // time = distance / speed
1573 f32 dtime_max_increment = position_max_increment / player_speed;
1575 // Maximum time increment is 10ms or lower
1576 if(dtime_max_increment > 0.01)
1577 dtime_max_increment = 0.01;
1579 // Don't allow overly huge dtime
1583 f32 dtime_downcount = dtime;
1586 Stuff that has a maximum time increment
1595 if(dtime_downcount > dtime_max_increment)
1597 dtime_part = dtime_max_increment;
1598 dtime_downcount -= dtime_part;
1602 dtime_part = dtime_downcount;
1604 Setting this to 0 (no -=dtime_part) disables an infinite loop
1605 when dtime_part is so small that dtime_downcount -= dtime_part
1608 dtime_downcount = 0;
1616 v3f lplayerpos = lplayer->getPosition();
1619 if(free_move == false && is_climbing == false && is_frozen == false)
1622 v3f speed = lplayer->getSpeed();
1623 if(lplayer->swimming_up == false)
1624 speed.Y -= 9.81 * BS * dtime_part * 2;
1627 if(lplayer->in_water_stable || lplayer->in_water)
1629 f32 max_down = 2.0*BS;
1630 if(speed.Y < -max_down) speed.Y = -max_down;
1633 if(speed.getLength() > max)
1635 speed = speed / speed.getLength() * max;
1639 lplayer->setSpeed(speed);
1644 This also does collision detection.
1646 lplayer->move(dtime_part, *m_map, position_max_increment,
1647 &player_collisions);
1650 while(dtime_downcount > 0.001);
1652 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
1654 for(core::list<CollisionInfo>::Iterator
1655 i = player_collisions.begin();
1656 i != player_collisions.end(); i++)
1658 CollisionInfo &info = *i;
1659 if(info.t == COLLISION_FALL)
1661 //f32 tolerance = BS*10; // 2 without damage
1662 f32 tolerance = BS*12; // 3 without damage
1664 if(info.speed > tolerance)
1666 f32 damage_f = (info.speed - tolerance)/BS*factor;
1667 u16 damage = (u16)(damage_f+0.5);
1668 if(lplayer->hp > damage)
1669 lplayer->hp -= damage;
1673 ClientEnvEvent event;
1674 event.type = CEE_PLAYER_DAMAGE;
1675 event.player_damage.amount = damage;
1676 m_client_event_queue.push_back(event);
1682 A quick draft of lava damage
1684 if(m_lava_hurt_interval.step(dtime, 1.0))
1686 v3f pf = lplayer->getPosition();
1688 // Feet, middle and head
1689 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
1690 MapNode n1 = m_map->getNodeNoEx(p1);
1691 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
1692 MapNode n2 = m_map->getNodeNoEx(p2);
1693 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
1694 MapNode n3 = m_map->getNodeNoEx(p2);
1696 u32 damage_per_second = 0;
1697 damage_per_second = MYMAX(damage_per_second,
1698 content_features(n1).damage_per_second);
1699 damage_per_second = MYMAX(damage_per_second,
1700 content_features(n2).damage_per_second);
1701 damage_per_second = MYMAX(damage_per_second,
1702 content_features(n3).damage_per_second);
1704 if(damage_per_second != 0)
1706 ClientEnvEvent event;
1707 event.type = CEE_PLAYER_DAMAGE;
1708 event.player_damage.amount = damage_per_second;
1709 m_client_event_queue.push_back(event);
1714 Stuff that can be done in an arbitarily large dtime
1716 for(core::list<Player*>::Iterator i = m_players.begin();
1717 i != m_players.end(); i++)
1719 Player *player = *i;
1720 v3f playerpos = player->getPosition();
1723 Handle non-local players
1725 if(player->isLocal() == false)
1728 player->move(dtime, *m_map, 100*BS);
1730 // Update lighting on remote players on client
1731 u8 light = LIGHT_MAX;
1734 v3s16 p = player->getLightPosition();
1735 MapNode n = m_map->getNode(p);
1736 light = n.getLightBlend(getDayNightRatio());
1738 catch(InvalidPositionException &e) {}
1739 player->updateLight(light);
1743 Add footsteps to grass
1747 // Get node that is at BS/4 under player
1748 v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
1750 MapNode n = m_map->getNode(bottompos);
1751 if(n.getContent() == CONTENT_GRASS)
1753 n.setContent(CONTENT_GRASS_FOOTSTEPS);
1754 m_map->setNode(bottompos, n);
1755 // Update mesh on client
1756 if(m_map->mapType() == MAPTYPE_CLIENT)
1758 v3s16 p_blocks = getNodeBlockPos(bottompos);
1759 MapBlock *b = m_map->getBlockNoCreate(p_blocks);
1760 //b->updateMesh(getDayNightRatio());
1761 b->setMeshExpired(true);
1765 catch(InvalidPositionException &e)
1772 Step active objects and update lighting of them
1775 for(core::map<u16, ClientActiveObject*>::Iterator
1776 i = m_active_objects.getIterator();
1777 i.atEnd()==false; i++)
1779 ClientActiveObject* obj = i.getNode()->getValue();
1781 obj->step(dtime, this);
1783 if(m_active_object_light_update_interval.step(dtime, 0.21))
1786 //u8 light = LIGHT_MAX;
1790 v3s16 p = obj->getLightPosition();
1791 MapNode n = m_map->getNode(p);
1792 light = n.getLightBlend(getDayNightRatio());
1794 catch(InvalidPositionException &e) {}
1795 obj->updateLight(light);
1800 void ClientEnvironment::updateMeshes(v3s16 blockpos)
1802 m_map->updateMeshes(blockpos, getDayNightRatio());
1805 void ClientEnvironment::expireMeshes(bool only_daynight_diffed)
1807 m_map->expireMeshes(only_daynight_diffed);
1810 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
1812 core::map<u16, ClientActiveObject*>::Node *n;
1813 n = m_active_objects.find(id);
1816 return n->getValue();
1819 bool isFreeClientActiveObjectId(u16 id,
1820 core::map<u16, ClientActiveObject*> &objects)
1825 for(core::map<u16, ClientActiveObject*>::Iterator
1826 i = objects.getIterator();
1827 i.atEnd()==false; i++)
1829 if(i.getNode()->getKey() == id)
1835 u16 getFreeClientActiveObjectId(
1836 core::map<u16, ClientActiveObject*> &objects)
1841 if(isFreeClientActiveObjectId(new_id, objects))
1851 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
1854 if(object->getId() == 0)
1856 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
1859 dstream<<"WARNING: ClientEnvironment::addActiveObject(): "
1860 <<"no free ids available"<<std::endl;
1864 object->setId(new_id);
1866 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
1868 dstream<<"WARNING: ClientEnvironment::addActiveObject(): "
1869 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1873 dstream<<"INFO: ClientEnvironment::addActiveObject(): "
1874 <<"added (id="<<object->getId()<<")"<<std::endl;
1875 m_active_objects.insert(object->getId(), object);
1876 object->addToScene(m_smgr);
1877 return object->getId();
1880 void ClientEnvironment::addActiveObject(u16 id, u8 type,
1881 const std::string &init_data)
1883 ClientActiveObject* obj = ClientActiveObject::create(type);
1886 dstream<<"WARNING: ClientEnvironment::addActiveObject(): "
1887 <<"id="<<id<<" type="<<type<<": Couldn't create object"
1894 addActiveObject(obj);
1896 obj->initialize(init_data);
1899 void ClientEnvironment::removeActiveObject(u16 id)
1901 dstream<<"ClientEnvironment::removeActiveObject(): "
1902 <<"id="<<id<<std::endl;
1903 ClientActiveObject* obj = getActiveObject(id);
1906 dstream<<"WARNING: ClientEnvironment::removeActiveObject(): "
1907 <<"id="<<id<<" not found"<<std::endl;
1910 obj->removeFromScene();
1912 m_active_objects.remove(id);
1915 void ClientEnvironment::processActiveObjectMessage(u16 id,
1916 const std::string &data)
1918 ClientActiveObject* obj = getActiveObject(id);
1921 dstream<<"WARNING: ClientEnvironment::processActiveObjectMessage():"
1922 <<" got message for id="<<id<<", which doesn't exist."
1926 obj->processMessage(data);
1930 Callbacks for activeobjects
1933 void ClientEnvironment::damageLocalPlayer(u8 damage)
1935 LocalPlayer *lplayer = getLocalPlayer();
1938 if(lplayer->hp > damage)
1939 lplayer->hp -= damage;
1943 ClientEnvEvent event;
1944 event.type = CEE_PLAYER_DAMAGE;
1945 event.player_damage.amount = damage;
1946 m_client_event_queue.push_back(event);
1950 Client likes to call these
1953 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
1954 core::array<DistanceSortedActiveObject> &dest)
1956 for(core::map<u16, ClientActiveObject*>::Iterator
1957 i = m_active_objects.getIterator();
1958 i.atEnd()==false; i++)
1960 ClientActiveObject* obj = i.getNode()->getValue();
1962 f32 d = (obj->getPosition() - origin).getLength();
1967 DistanceSortedActiveObject dso(obj, d);
1969 dest.push_back(dso);
1973 ClientEnvEvent ClientEnvironment::getClientEvent()
1975 if(m_client_event_queue.size() == 0)
1977 ClientEnvEvent event;
1978 event.type = CEE_NONE;
1981 return m_client_event_queue.pop_front();
1984 void ClientEnvironment::drawPostFx(video::IVideoDriver* driver, v3f camera_pos)
1986 /*LocalPlayer *player = getLocalPlayer();
1988 v3f pos_f = player->getPosition() + v3f(0,BS*1.625,0);*/
1989 v3f pos_f = camera_pos;
1990 v3s16 p_nodes = floatToInt(pos_f, BS);
1991 MapNode n = m_map->getNodeNoEx(p_nodes);
1992 if(n.getContent() == CONTENT_WATER || n.getContent() == CONTENT_WATERSOURCE)
1994 v2u32 ss = driver->getScreenSize();
1995 core::rect<s32> rect(0,0, ss.X, ss.Y);
1996 driver->draw2DRectangle(video::SColor(64, 100, 100, 200), rect);
1998 else if(content_features(n).solidness == 2 &&
1999 g_settings.getBool("free_move") == false)
2001 v2u32 ss = driver->getScreenSize();
2002 core::rect<s32> rect(0,0, ss.X, ss.Y);
2003 driver->draw2DRectangle(video::SColor(255, 0, 0, 0), rect);
2007 #endif // #ifndef SERVER