diff --git a/.hgignore b/.hgignore index 0870e3c67..964b22bd9 100644 --- a/.hgignore +++ b/.hgignore @@ -9,7 +9,12 @@ src/jthread/CMakeFiles/* src/jthread/Makefile src/jthread/cmake_config.h src/jthread/cmake_install.cmake +src/.*.swp +src/sqlite/libsqlite3.a +src/session.vim +util/uloste.png minetest.conf +debug.txt bin/ CMakeCache.txt CPackConfig.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 854c4b48d..15f4a6453 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,7 +9,7 @@ project(minetest) set(VERSION_MAJOR 0) set(VERSION_MINOR 2) -set(VERSION_PATCH 20110618_0_dev) +set(VERSION_PATCH 20110704_0) set(VERSION_STRING "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}") # Configuration options diff --git a/data/oerkki1_damaged.png b/data/oerkki1_damaged.png new file mode 100644 index 000000000..9b777387c Binary files /dev/null and b/data/oerkki1_damaged.png differ diff --git a/data/unknown_block.png b/data/unknown_block.png new file mode 100644 index 000000000..a27cb8ca9 Binary files /dev/null and b/data/unknown_block.png differ diff --git a/doc/changelog.txt b/doc/changelog.txt index 5a01b6bc4..58d28d236 100644 --- a/doc/changelog.txt +++ b/doc/changelog.txt @@ -3,6 +3,11 @@ Minetest-c55 changelog This should contain all the major changes. For minor stuff, refer to the commit log of the repository. +2011-07-04: +- Many small fixes +- Code reorganizing to aid further development +- Renewed map generator + 2011-06-02: - Password crash on windows fixed - Optimized server CPU usage a lot diff --git a/minetest.conf.example b/minetest.conf.example index 6e8a82bac..b81cc5fc6 100644 --- a/minetest.conf.example +++ b/minetest.conf.example @@ -9,25 +9,28 @@ # # Further documentation: # http://celeron.55.lt/~celeron55/minetest/wiki/doku.php +# +# NOTE: This file might not be up-to-date, refer to the +# defaultsettings.cpp file for an up-to-date list: +# https://bitbucket.org/celeron55/minetest/src/tip/src/defaultsettings.cpp +# +# A vim command to convert most of defaultsettings.cpp to conf file format: +# :'<,'>s/\tg_settings\.setDefault("\([^"]*\)", "\([^"]*\)");.*/#\1 = \2/g # -# Client side stuff +# Client and server # -# Initial window size -#screenW = 800 -#screenH = 600 - -# Port to connect to and to bind a server at -#port = 30000 - -# Address to connect to (blank = start local server) -#address = - -# Name of player. On server, this is the default admin. +# Network port (UDP) +#port = +# Name of player; on a server this is the main admin #name = -# Key configuration. +# +# Client stuff +# + +# Key mappings # See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 #keymap_forward = KEY_KEY_W #keymap_backward = KEY_KEY_S @@ -38,108 +41,99 @@ #keymap_inventory = KEY_KEY_I #keymap_chat = KEY_KEY_T #keymap_rangeselect = KEY_KEY_R -# Some (temporary) keys for debugging features +#keymap_freemove = KEY_KEY_K +#keymap_fastmove = KEY_KEY_J +#keymap_frametime_graph = KEY_F1 +#keymap_screenshot = KEY_F12 +# Some (temporary) keys for debugging #keymap_special1 = KEY_KEY_E #keymap_print_debug_stacks = KEY_KEY_P -#invert_mouse = false - # The desired FPS #wanted_fps = 30 - # If FPS would go higher than this, limit it by sleeping # (to not waste CPU power for no benefit) #fps_max = 60 - # The allowed adjustment range for the automatic rendering range adjustment #viewing_range_nodes_max = 300 -#viewing_range_nodes_min = 35 - +#viewing_range_nodes_min = 25 +# Initial window size +screenW# = 800 +screenH# = 600 +# Address to connect to (#blank = start local server) +#address = +# Enable random user input, for testing +#random_input = false +# Timeout for client to remove unused map data from memory +#client_unload_unused_data_timeout = 600 # Whether to fog out the end of the visible area #enable_fog = true - -# Enable/disable clouds -#enable_clouds = true - -# Experimental -#enable_farmesh = false - -# Enable a bit lower water surface; disable for speed -#new_style_water = true - +# Enable a bit lower water surface; disable for speed (not quite optimized) +#new_style_water = false # Enable nice leaves; disable for speed #new_style_leaves = true - # Enable smooth lighting with simple ambient occlusion; # disable for speed or for different looks. #smooth_lighting = true - # Whether to draw a frametime graph (for debugging frametime) #frametime_graph = false - # Enable combining mainly used textures to a bigger one for improved speed # disable if it causes graphics glitches. #enable_texture_atlas = true - # Path to texture directory. All textures are first searched from here. -#texture_path = - +#texture_path = # Video back-end. # Possible values: null, software, burningsvideo, direct3d8, direct3d9, opengl #video_driver = opengl - -# Enable random user input, for testing -#random_input = false - -# Timeout for client to remove unused map data from memory -#client_delete_unused_sectors_timeout = 1200 +# Unobstructed movement without physics, downwards key is keymap_special1 +#free_move = false +# Continuous forward movement (for testing) +#continuous_forward = false +# Fast movement (keymap_special1) +#fast_move = false +# Invert mouse +#invert_mouse = false +# FarMesh thingy +#enable_farmesh = false +# Enable/disable clouds +#enable_clouds = true +# Don't draw stone (for testing) +#invisible_stone = false +# Path for screenshots +#screenshot_path = . # -# Server side stuff +# Server stuff # # Map directory (everything in the world is stored here) -#map-dir = /home/palle/custom_map - +#map-#dir = /custom/map +# Set to true to enable experimental features or stuff that is tested +# (varies from version to version, usually not useful at all) +#enable_experimental = false # Set to true to enable creative mode (unlimited inventory) #creative_mode = false - #enable_damage = false - -#default_password = - -# Available privileges: build, teleport, settime, privs, shout -#default_privs = build, shout - # Gives some stuff to players at the beginning #give_initial_stuff = false - -# Set to true to enable experimental features -# (varies from version to version, see wiki) -#enable_experimental = false - -# Profiler data print interval. 0 = disable. -#profiler_print_interval = 10 +#default_password = +# Available privileges: build, teleport, settime, privs, shout +#default_privs = build, shout +# Profiler data print interval. #0 = disable. +#profiler_print_interval = 0 +#enable_mapgen_debug_info = false # Player and object positions are sent at intervals specified by this -#objectdata_inverval = 0.2 - +#objectdata_interval = 0.2 #active_object_range = 2 - -#max_simultaneous_block_sends_per_client = 1 -#max_simultaneous_block_sends_server_total = 4 - -#max_block_send_distance = 5 -#max_block_generate_distance = 4 - -# 20 min/day +#max_simultaneous_block_sends_per_client = 2 +#max_simultaneous_block_sends_server_total = 8 +#max_block_send_distance = 8 +#max_block_generate_distance = 8 +#time_send_interval = 20 +# Length of day/night cycle. 72=20min, 360=4min, 1=24hour #time_speed = 72 -# 4 min/day -#time_speed = 360 -# 1 min/day -#time_speed = 1440 - -#time_send_interval = 5 -#server_unload_unused_sectors_timeout = 60 +#server_unload_unused_data_timeout = 60 #server_map_save_interval = 60 +#full_block_send_enable_min_time_from_building = 2.0 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f62a8626b..dfe5b5753 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -61,6 +61,7 @@ configure_file( ) set(common_SRCS + content_sao.cpp mapgen.cpp content_inventory.cpp content_nodemeta.cpp @@ -102,6 +103,7 @@ set(common_SRCS # Client sources set(minetest_SRCS ${common_SRCS} + content_cao.cpp mapblock_mesh.cpp farmesh.cpp keycode.cpp diff --git a/src/activeobject.h b/src/activeobject.h index 382f7f798..09ee23a14 100644 --- a/src/activeobject.h +++ b/src/activeobject.h @@ -23,6 +23,9 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "common_irrlicht.h" #include +#define ACTIVEOBJECT_TYPE_INVALID 0 +// Other types are defined in content_object.h + struct ActiveObjectMessage { ActiveObjectMessage(u16 id_, bool reliable_=true, std::string data_=""): @@ -36,12 +39,6 @@ struct ActiveObjectMessage std::string datastring; }; -#define ACTIVEOBJECT_TYPE_INVALID 0 -#define ACTIVEOBJECT_TYPE_TEST 1 -#define ACTIVEOBJECT_TYPE_ITEM 2 -#define ACTIVEOBJECT_TYPE_RAT 3 -#define ACTIVEOBJECT_TYPE_OERKKI1 4 - /* Parent class for ServerActiveObject and ClientActiveObject */ diff --git a/src/client.cpp b/src/client.cpp index e86b3a4f8..4f0baa573 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -25,6 +25,105 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "main.h" #include #include "porting.h" +#include "mapsector.h" +#include "mapblock_mesh.h" +#include "mapblock.h" + +/* + QueuedMeshUpdate +*/ + +QueuedMeshUpdate::QueuedMeshUpdate(): + p(-1337,-1337,-1337), + data(NULL), + ack_block_to_server(false) +{ +} + +QueuedMeshUpdate::~QueuedMeshUpdate() +{ + if(data) + delete data; +} + +/* + MeshUpdateQueue +*/ + +MeshUpdateQueue::MeshUpdateQueue() +{ + m_mutex.Init(); +} + +MeshUpdateQueue::~MeshUpdateQueue() +{ + JMutexAutoLock lock(m_mutex); + + core::list::Iterator i; + for(i=m_queue.begin(); i!=m_queue.end(); i++) + { + QueuedMeshUpdate *q = *i; + delete q; + } +} + +/* + peer_id=0 adds with nobody to send to +*/ +void MeshUpdateQueue::addBlock(v3s16 p, MeshMakeData *data, bool ack_block_to_server) +{ + DSTACK(__FUNCTION_NAME); + + assert(data); + + JMutexAutoLock lock(m_mutex); + + /* + Find if block is already in queue. + If it is, update the data and quit. + */ + core::list::Iterator i; + for(i=m_queue.begin(); i!=m_queue.end(); i++) + { + QueuedMeshUpdate *q = *i; + if(q->p == p) + { + if(q->data) + delete q->data; + q->data = data; + if(ack_block_to_server) + q->ack_block_to_server = true; + return; + } + } + + /* + Add the block + */ + QueuedMeshUpdate *q = new QueuedMeshUpdate; + q->p = p; + q->data = data; + q->ack_block_to_server = ack_block_to_server; + m_queue.push_back(q); +} + +// Returned pointer must be deleted +// Returns NULL if queue is empty +QueuedMeshUpdate * MeshUpdateQueue::pop() +{ + JMutexAutoLock lock(m_mutex); + + core::list::Iterator i = m_queue.begin(); + if(i == m_queue.end()) + return NULL; + QueuedMeshUpdate *q = *i; + m_queue.erase(i); + return q; +} + +/* + MeshUpdateThread +*/ void * MeshUpdateThread::Thread() { @@ -36,6 +135,15 @@ void * MeshUpdateThread::Thread() while(getRun()) { + /*// Wait for output queue to flush. + // Allow 2 in queue, this makes less frametime jitter. + // Umm actually, there is no much difference + if(m_queue_out.size() >= 2) + { + sleep_ms(3); + continue; + }*/ + QueuedMeshUpdate *q = m_queue_in.pop(); if(q == NULL) { @@ -91,7 +199,7 @@ Client::Client( m_access_denied(false) { m_packetcounter_timer = 0.0; - m_delete_unused_sectors_timer = 0.0; + //m_delete_unused_sectors_timer = 0.0; m_connection_reinit_timer = 0.0; m_avg_rtt_timer = 0.0; m_playerpos_send_timer = 0.0; @@ -195,7 +303,11 @@ void Client::step(float dtime) m_packetcounter.clear(); } } + + // Get connection status + bool connected = connectedAndInitialized(); +#if 0 { /* Delete unused sectors @@ -225,16 +337,16 @@ void Client::step(float dtime) true, &deleted_blocks);*/ // Delete whole sectors - u32 num = m_env.getMap().unloadUnusedData + m_env.getMap().unloadUnusedData (delete_unused_sectors_timeout, - false, &deleted_blocks); + &deleted_blocks); - if(num > 0) + if(deleted_blocks.size() > 0) { /*dstream< deleted_blocks; + m_env.getMap().timerUpdate(map_timer_and_unload_dtime, + g_settings.getFloat("client_unload_unused_data_timeout"), + &deleted_blocks); + + /*if(deleted_blocks.size() > 0) + dstream<<"Client: Unloaded "<::Iterator i = deleted_blocks.begin(); + core::list sendlist; + for(;;) + { + if(sendlist.size() == 255 || i == deleted_blocks.end()) + { + if(sendlist.size() == 0) + break; + /* + [0] u16 command + [2] u8 count + [3] v3s16 pos_0 + [3+6] v3s16 pos_1 + ... + */ + u32 replysize = 2+1+6*sendlist.size(); + SharedBuffer reply(replysize); + writeU16(&reply[0], TOSERVER_DELETEDBLOCKS); + reply[2] = sendlist.size(); + u32 k = 0; + for(core::list::Iterator + j = sendlist.begin(); + j != sendlist.end(); j++) + { + writeV3S16(&reply[2+1+6*k], *j); + k++; + } + m_con.Send(PEER_ID_SERVER, 1, reply, true); + + if(i == deleted_blocks.end()) + break; + + sendlist.clear(); + } + + sendlist.push_back(*i); + i++; + } + } + /* Handle environment */ @@ -345,23 +517,23 @@ void Client::step(float dtime) //TimeTaker envtimer("env step", m_device); // Step environment m_env.step(dtime); - - // Step active blocks + + /* + Handle active blocks + NOTE: These old objects are DEPRECATED. TODO: Remove + */ for(core::map::Iterator i = m_active_blocks.getIterator(); i.atEnd() == false; i++) { v3s16 p = i.getNode()->getKey(); - MapBlock *block = NULL; - try - { - block = m_env.getMap().getBlockNoCreate(p); - block->stepObjects(dtime, false, m_env.getDayNightRatio()); - } - catch(InvalidPositionException &e) - { - } + MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(p); + if(block == NULL) + continue; + + // Step MapBlockObjects + block->stepObjects(dtime, false, m_env.getDayNightRatio()); } /* @@ -695,78 +867,43 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id) MapSector *sector; MapBlock *block; - { //envlock - //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out - - v2s16 p2d(p.X, p.Z); - sector = m_env.getMap().emergeSector(p2d); - - v2s16 sp = sector->getPos(); - if(sp != p2d) - { - dstream<<"ERROR: Got sector with getPos()=" - <<"("<getPos() == p2d); - assert(sp == p2d); - //assert(sector->getPos() == p2d); + //TimeTaker timer("MapBlock deSerialize"); + // 0ms + + block = sector->getBlockNoCreateNoEx(p.Y); + if(block) + { + /* + Update an existing block + */ + //dstream<<"Updating"<deSerialize(istr, ser_version); + } + else + { + /* + Create a new block + */ + //dstream<<"Creating new"<deSerialize(istr, ser_version); + sector->insertBlock(block); - //TimeTaker timer("MapBlock deSerialize"); - // 0ms - - try{ - block = sector->getBlockNoCreate(p.Y); - /* - Update an existing block - */ - //dstream<<"Updating"<deSerialize(istr, ser_version); - //block->setChangedFlag(); - } - catch(InvalidPositionException &e) - { - /* - Create a new block - */ - //dstream<<"Creating new"<deSerialize(istr, ser_version); - sector->insertBlock(block); - //block->setChangedFlag(); - - //DEBUG - /*NodeMod mod; - mod.type = NODEMOD_CHANGECONTENT; - mod.param = CONTENT_MESE; - block->setTempMod(v3s16(8,10,8), mod); - block->setTempMod(v3s16(8,9,8), mod); - block->setTempMod(v3s16(8,8,8), mod); - block->setTempMod(v3s16(8,7,8), mod); - block->setTempMod(v3s16(8,6,8), mod);*/ -#if 0 - /* - Add some coulds - Well, this is a dumb way to do it, they should just - be drawn as separate objects. But the looks of them - can be tested this way. - */ - if(p.Y == 3) - { - NodeMod mod; - mod.type = NODEMOD_CHANGECONTENT; - mod.param = CONTENT_CLOUD; - v3s16 p2; - p2.Y = 8; - for(p2.X=3; p2.X<=13; p2.X++) - for(p2.Z=3; p2.Z<=13; p2.Z++) - { - block->setTempMod(p2, mod); - } - } -#endif - } - } //envlock + //DEBUG + /*NodeMod mod; + mod.type = NODEMOD_CHANGECONTENT; + mod.param = CONTENT_MESE; + block->setTempMod(v3s16(8,10,8), mod); + block->setTempMod(v3s16(8,9,8), mod); + block->setTempMod(v3s16(8,8,8), mod); + block->setTempMod(v3s16(8,7,8), mod); + block->setTempMod(v3s16(8,6,8), mod);*/ + } #if 0 /* @@ -798,6 +935,7 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id) /* Add it to mesh update queue and set it to be acknowledged after update. */ + //std::cerr<<"Adding mesh update task for received block"<= 0 && m_daynight_i < DAYNIGHT_CACHE_COUNT); - return m_daynight_i; -}*/ - u32 Client::getDayNightRatio() { //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out @@ -2000,6 +2136,40 @@ u16 Client::getHP() return player->hp; } +void Client::setTempMod(v3s16 p, NodeMod mod) +{ + //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out + assert(m_env.getMap().mapType() == MAPTYPE_CLIENT); + + core::map affected_blocks; + ((ClientMap&)m_env.getMap()).setTempMod(p, mod, + &affected_blocks); + + for(core::map::Iterator + i = affected_blocks.getIterator(); + i.atEnd() == false; i++) + { + i.getNode()->getValue()->updateMesh(m_env.getDayNightRatio()); + } +} + +void Client::clearTempMod(v3s16 p) +{ + //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out + assert(m_env.getMap().mapType() == MAPTYPE_CLIENT); + + core::map affected_blocks; + ((ClientMap&)m_env.getMap()).clearTempMod(p, + &affected_blocks); + + for(core::map::Iterator + i = affected_blocks.getIterator(); + i.atEnd() == false; i++) + { + i.getNode()->getValue()->updateMesh(m_env.getDayNightRatio()); + } +} + void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server) { /*dstream<<"Client::addUpdateMeshTask(): " @@ -2009,7 +2179,7 @@ void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server) MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p); if(b == NULL) return; - + /* Create a task to update the mesh of the block */ @@ -2018,7 +2188,8 @@ void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server) { //TimeTaker timer("data fill"); - // 0ms + // Release: ~0ms + // Debug: 1-6ms, avg=2ms data->fill(getDayNightRatio(), b); } @@ -2044,6 +2215,10 @@ void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server) } #endif + /* + Mark mesh as non-expired at this point so that it can already + be marked as expired again if the data changes + */ b->setMeshExpired(false); } diff --git a/src/client.h b/src/client.h index a1b1c66b4..0150b687e 100644 --- a/src/client.h +++ b/src/client.h @@ -28,6 +28,9 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "jmutex.h" #include #include "clientobject.h" +#include "utility.h" // For IntervalLimiter + +struct MeshMakeData; class ClientNotReadyException : public BaseException { @@ -43,18 +46,8 @@ struct QueuedMeshUpdate MeshMakeData *data; bool ack_block_to_server; - QueuedMeshUpdate(): - p(-1337,-1337,-1337), - data(NULL), - ack_block_to_server(false) - { - } - - ~QueuedMeshUpdate() - { - if(data) - delete data; - } + QueuedMeshUpdate(); + ~QueuedMeshUpdate(); }; /* @@ -63,76 +56,18 @@ struct QueuedMeshUpdate class MeshUpdateQueue { public: - MeshUpdateQueue() - { - m_mutex.Init(); - } + MeshUpdateQueue(); - ~MeshUpdateQueue() - { - JMutexAutoLock lock(m_mutex); - - core::list::Iterator i; - for(i=m_queue.begin(); i!=m_queue.end(); i++) - { - QueuedMeshUpdate *q = *i; - delete q; - } - } + ~MeshUpdateQueue(); /* peer_id=0 adds with nobody to send to */ - void addBlock(v3s16 p, MeshMakeData *data, bool ack_block_to_server) - { - DSTACK(__FUNCTION_NAME); - - assert(data); - - JMutexAutoLock lock(m_mutex); - - /* - Find if block is already in queue. - If it is, update the data and quit. - */ - core::list::Iterator i; - for(i=m_queue.begin(); i!=m_queue.end(); i++) - { - QueuedMeshUpdate *q = *i; - if(q->p == p) - { - if(q->data) - delete q->data; - q->data = data; - if(ack_block_to_server) - q->ack_block_to_server = true; - return; - } - } - - /* - Add the block - */ - QueuedMeshUpdate *q = new QueuedMeshUpdate; - q->p = p; - q->data = data; - q->ack_block_to_server = ack_block_to_server; - m_queue.push_back(q); - } + void addBlock(v3s16 p, MeshMakeData *data, bool ack_block_to_server); // Returned pointer must be deleted // Returns NULL if queue is empty - QueuedMeshUpdate * pop() - { - JMutexAutoLock lock(m_mutex); - - core::list::Iterator i = m_queue.begin(); - if(i == m_queue.end()) - return NULL; - QueuedMeshUpdate *q = *i; - m_queue.erase(i); - return q; - } + QueuedMeshUpdate * pop(); u32 size() { @@ -309,40 +244,8 @@ public: u16 getHP(); - //void updateSomeExpiredMeshes(); - - void setTempMod(v3s16 p, NodeMod mod) - { - //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out - assert(m_env.getMap().mapType() == MAPTYPE_CLIENT); - - core::map affected_blocks; - ((ClientMap&)m_env.getMap()).setTempMod(p, mod, - &affected_blocks); - - for(core::map::Iterator - i = affected_blocks.getIterator(); - i.atEnd() == false; i++) - { - i.getNode()->getValue()->updateMesh(m_env.getDayNightRatio()); - } - } - void clearTempMod(v3s16 p) - { - //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out - assert(m_env.getMap().mapType() == MAPTYPE_CLIENT); - - core::map affected_blocks; - ((ClientMap&)m_env.getMap()).clearTempMod(p, - &affected_blocks); - - for(core::map::Iterator - i = affected_blocks.getIterator(); - i.atEnd() == false; i++) - { - i.getNode()->getValue()->updateMesh(m_env.getDayNightRatio()); - } - } + void setTempMod(v3s16 p, NodeMod mod); + void clearTempMod(v3s16 p); float getAvgRtt() { @@ -389,6 +292,15 @@ public: { return m_access_denied_reason; } + + /* + This should only be used for calling the special drawing stuff in + ClientEnvironment + */ + ClientEnvironment * getEnv() + { + return &m_env; + } private: @@ -404,11 +316,11 @@ private: void sendPlayerInfo(); float m_packetcounter_timer; - float m_delete_unused_sectors_timer; float m_connection_reinit_timer; float m_avg_rtt_timer; float m_playerpos_send_timer; float m_ignore_damage_timer; // Used after server moves player + IntervalLimiter m_map_timer_and_unload_interval; MeshUpdateThread m_mesh_update_thread; diff --git a/src/clientobject.cpp b/src/clientobject.cpp index 402535ffc..787efef29 100644 --- a/src/clientobject.cpp +++ b/src/clientobject.cpp @@ -21,9 +21,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "debug.h" #include "porting.h" #include "constants.h" -#include "utility.h" -#include "environment.h" -#include "tile.h" /* ClientActiveObject @@ -68,674 +65,4 @@ void ClientActiveObject::registerType(u16 type, Factory f) m_types.insert(type, f); } -/* - TestCAO -*/ - -// Prototype -TestCAO proto_TestCAO; - -TestCAO::TestCAO(): - ClientActiveObject(0), - m_node(NULL), - m_position(v3f(0,10*BS,0)) -{ - ClientActiveObject::registerType(getType(), create); -} - -TestCAO::~TestCAO() -{ -} - -ClientActiveObject* TestCAO::create() -{ - return new TestCAO(); -} - -void TestCAO::addToScene(scene::ISceneManager *smgr) -{ - if(m_node != NULL) - return; - - video::IVideoDriver* driver = smgr->getVideoDriver(); - - scene::SMesh *mesh = new scene::SMesh(); - scene::IMeshBuffer *buf = new scene::SMeshBuffer(); - video::SColor c(255,255,255,255); - video::S3DVertex vertices[4] = - { - video::S3DVertex(-BS/2,-BS/4,0, 0,0,0, c, 0,1), - video::S3DVertex(BS/2,-BS/4,0, 0,0,0, c, 1,1), - video::S3DVertex(BS/2,BS/4,0, 0,0,0, c, 1,0), - video::S3DVertex(-BS/2,BS/4,0, 0,0,0, c, 0,0), - }; - u16 indices[] = {0,1,2,2,3,0}; - buf->append(vertices, 4, indices, 6); - // Set material - buf->getMaterial().setFlag(video::EMF_LIGHTING, false); - buf->getMaterial().setFlag(video::EMF_BACK_FACE_CULLING, false); - buf->getMaterial().setTexture - (0, driver->getTexture(getTexturePath("rat.png").c_str())); - buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false); - buf->getMaterial().setFlag(video::EMF_FOG_ENABLE, true); - buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; - // Add to mesh - mesh->addMeshBuffer(buf); - buf->drop(); - m_node = smgr->addMeshSceneNode(mesh, NULL); - mesh->drop(); - updateNodePos(); -} - -void TestCAO::removeFromScene() -{ - if(m_node == NULL) - return; - - m_node->remove(); - m_node = NULL; -} - -void TestCAO::updateLight(u8 light_at_pos) -{ -} - -v3s16 TestCAO::getLightPosition() -{ - return floatToInt(m_position, BS); -} - -void TestCAO::updateNodePos() -{ - if(m_node == NULL) - return; - - m_node->setPosition(m_position); - //m_node->setRotation(v3f(0, 45, 0)); -} - -void TestCAO::step(float dtime, ClientEnvironment *env) -{ - if(m_node) - { - v3f rot = m_node->getRotation(); - //dstream<<"dtime="<>cmd; - if(cmd == 0) - { - v3f newpos; - is>>newpos.X; - is>>newpos.Y; - is>>newpos.Z; - m_position = newpos; - updateNodePos(); - } -} - -/* - ItemCAO -*/ - -#include "inventory.h" - -// Prototype -ItemCAO proto_ItemCAO; - -ItemCAO::ItemCAO(): - ClientActiveObject(0), - m_selection_box(-BS/3.,0.0,-BS/3., BS/3.,BS*2./3.,BS/3.), - m_node(NULL), - m_position(v3f(0,10*BS,0)) -{ - ClientActiveObject::registerType(getType(), create); -} - -ItemCAO::~ItemCAO() -{ -} - -ClientActiveObject* ItemCAO::create() -{ - return new ItemCAO(); -} - -void ItemCAO::addToScene(scene::ISceneManager *smgr) -{ - if(m_node != NULL) - return; - - video::IVideoDriver* driver = smgr->getVideoDriver(); - - scene::SMesh *mesh = new scene::SMesh(); - scene::IMeshBuffer *buf = new scene::SMeshBuffer(); - video::SColor c(255,255,255,255); - video::S3DVertex vertices[4] = - { - /*video::S3DVertex(-BS/2,-BS/4,0, 0,0,0, c, 0,1), - video::S3DVertex(BS/2,-BS/4,0, 0,0,0, c, 1,1), - video::S3DVertex(BS/2,BS/4,0, 0,0,0, c, 1,0), - video::S3DVertex(-BS/2,BS/4,0, 0,0,0, c, 0,0),*/ - video::S3DVertex(BS/3.,0,0, 0,0,0, c, 0,1), - video::S3DVertex(-BS/3.,0,0, 0,0,0, c, 1,1), - video::S3DVertex(-BS/3.,0+BS*2./3.,0, 0,0,0, c, 1,0), - video::S3DVertex(BS/3.,0+BS*2./3.,0, 0,0,0, c, 0,0), - }; - u16 indices[] = {0,1,2,2,3,0}; - buf->append(vertices, 4, indices, 6); - // Set material - buf->getMaterial().setFlag(video::EMF_LIGHTING, false); - buf->getMaterial().setFlag(video::EMF_BACK_FACE_CULLING, false); - //buf->getMaterial().setTexture(0, NULL); - // Initialize with the stick texture - buf->getMaterial().setTexture - (0, driver->getTexture(getTexturePath("stick.png").c_str())); - buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false); - buf->getMaterial().setFlag(video::EMF_FOG_ENABLE, true); - buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; - // Add to mesh - mesh->addMeshBuffer(buf); - buf->drop(); - m_node = smgr->addMeshSceneNode(mesh, NULL); - mesh->drop(); - // Set it to use the materials of the meshbuffers directly. - // This is needed for changing the texture in the future - m_node->setReadOnlyMaterials(true); - updateNodePos(); -} - -void ItemCAO::removeFromScene() -{ - if(m_node == NULL) - return; - - m_node->remove(); - m_node = NULL; -} - -void ItemCAO::updateLight(u8 light_at_pos) -{ - if(m_node == NULL) - return; - - u8 li = decode_light(light_at_pos); - video::SColor color(255,li,li,li); - - scene::IMesh *mesh = m_node->getMesh(); - if(mesh == NULL) - return; - - u16 mc = mesh->getMeshBufferCount(); - for(u16 j=0; jgetMeshBuffer(j); - video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices(); - u16 vc = buf->getVertexCount(); - for(u16 i=0; isetPosition(m_position); -} - -void ItemCAO::step(float dtime, ClientEnvironment *env) -{ - if(m_node) - { - /*v3f rot = m_node->getRotation(); - rot.Y += dtime * 120; - m_node->setRotation(rot);*/ - LocalPlayer *player = env->getLocalPlayer(); - assert(player); - v3f rot = m_node->getRotation(); - rot.Y = 180.0 - (player->getYaw()); - m_node->setRotation(rot); - } -} - -void ItemCAO::processMessage(const std::string &data) -{ - dstream<<"ItemCAO: Got message"<getMesh(); - - if(mesh == NULL) - return; - - scene::IMeshBuffer *buf = mesh->getMeshBuffer(0); - - if(buf == NULL) - return; - - // Create an inventory item to see what is its image - std::istringstream is(m_inventorystring, std::ios_base::binary); - video::ITexture *texture = NULL; - try{ - InventoryItem *item = NULL; - item = InventoryItem::deSerialize(is); - dstream<<__FUNCTION_NAME<<": m_inventorystring=\"" - < item="<getImage(); - delete item; - } - } - catch(SerializationError &e) - { - dstream<<"WARNING: "<<__FUNCTION_NAME - <<": error deSerializing inventorystring \"" - <getMaterial().setTexture(0, texture); - -} - -/* - RatCAO -*/ - -#include "inventory.h" - -// Prototype -RatCAO proto_RatCAO; - -RatCAO::RatCAO(): - ClientActiveObject(0), - m_selection_box(-BS/3.,0.0,-BS/3., BS/3.,BS/2.,BS/3.), - m_node(NULL), - m_position(v3f(0,10*BS,0)), - m_yaw(0) -{ - ClientActiveObject::registerType(getType(), create); -} - -RatCAO::~RatCAO() -{ -} - -ClientActiveObject* RatCAO::create() -{ - return new RatCAO(); -} - -void RatCAO::addToScene(scene::ISceneManager *smgr) -{ - if(m_node != NULL) - return; - - video::IVideoDriver* driver = smgr->getVideoDriver(); - - scene::SMesh *mesh = new scene::SMesh(); - scene::IMeshBuffer *buf = new scene::SMeshBuffer(); - video::SColor c(255,255,255,255); - video::S3DVertex vertices[4] = - { - video::S3DVertex(-BS/2,0,0, 0,0,0, c, 0,1), - video::S3DVertex(BS/2,0,0, 0,0,0, c, 1,1), - video::S3DVertex(BS/2,BS/2,0, 0,0,0, c, 1,0), - video::S3DVertex(-BS/2,BS/2,0, 0,0,0, c, 0,0), - }; - u16 indices[] = {0,1,2,2,3,0}; - buf->append(vertices, 4, indices, 6); - // Set material - buf->getMaterial().setFlag(video::EMF_LIGHTING, false); - buf->getMaterial().setFlag(video::EMF_BACK_FACE_CULLING, false); - //buf->getMaterial().setTexture(0, NULL); - buf->getMaterial().setTexture - (0, driver->getTexture(getTexturePath("rat.png").c_str())); - buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false); - buf->getMaterial().setFlag(video::EMF_FOG_ENABLE, true); - buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; - // Add to mesh - mesh->addMeshBuffer(buf); - buf->drop(); - m_node = smgr->addMeshSceneNode(mesh, NULL); - mesh->drop(); - // Set it to use the materials of the meshbuffers directly. - // This is needed for changing the texture in the future - m_node->setReadOnlyMaterials(true); - updateNodePos(); -} - -void RatCAO::removeFromScene() -{ - if(m_node == NULL) - return; - - m_node->remove(); - m_node = NULL; -} - -void RatCAO::updateLight(u8 light_at_pos) -{ - if(m_node == NULL) - return; - - u8 li = decode_light(light_at_pos); - video::SColor color(255,li,li,li); - - scene::IMesh *mesh = m_node->getMesh(); - if(mesh == NULL) - return; - - u16 mc = mesh->getMeshBufferCount(); - for(u16 j=0; jgetMeshBuffer(j); - video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices(); - u16 vc = buf->getVertexCount(); - for(u16 i=0; isetPosition(m_position); - m_node->setPosition(pos_translator.vect_show); - - v3f rot = m_node->getRotation(); - rot.Y = 180.0 - m_yaw; - m_node->setRotation(rot); -} - -void RatCAO::step(float dtime, ClientEnvironment *env) -{ - pos_translator.translate(dtime); - updateNodePos(); -} - -void RatCAO::processMessage(const std::string &data) -{ - //dstream<<"RatCAO: Got message"<getVideoDriver(); - - scene::SMesh *mesh = new scene::SMesh(); - scene::IMeshBuffer *buf = new scene::SMeshBuffer(); - video::SColor c(255,255,255,255); - video::S3DVertex vertices[4] = - { - video::S3DVertex(-BS/2,0,0, 0,0,0, c, 0,1), - video::S3DVertex(BS/2,0,0, 0,0,0, c, 1,1), - video::S3DVertex(BS/2,BS*2,0, 0,0,0, c, 1,0), - video::S3DVertex(-BS/2,BS*2,0, 0,0,0, c, 0,0), - }; - u16 indices[] = {0,1,2,2,3,0}; - buf->append(vertices, 4, indices, 6); - // Set material - buf->getMaterial().setFlag(video::EMF_LIGHTING, false); - buf->getMaterial().setFlag(video::EMF_BACK_FACE_CULLING, false); - //buf->getMaterial().setTexture(0, NULL); - buf->getMaterial().setTexture - (0, driver->getTexture(getTexturePath("oerkki1.png").c_str())); - buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false); - buf->getMaterial().setFlag(video::EMF_FOG_ENABLE, true); - buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; - // Add to mesh - mesh->addMeshBuffer(buf); - buf->drop(); - m_node = smgr->addMeshSceneNode(mesh, NULL); - mesh->drop(); - // Set it to use the materials of the meshbuffers directly. - // This is needed for changing the texture in the future - m_node->setReadOnlyMaterials(true); - updateNodePos(); -} - -void Oerkki1CAO::removeFromScene() -{ - if(m_node == NULL) - return; - - m_node->remove(); - m_node = NULL; -} - -void Oerkki1CAO::updateLight(u8 light_at_pos) -{ - if(m_node == NULL) - return; - - if(light_at_pos <= 2) - { - m_node->setVisible(false); - return; - } - - m_node->setVisible(true); - - u8 li = decode_light(light_at_pos); - video::SColor color(255,li,li,li); - - scene::IMesh *mesh = m_node->getMesh(); - if(mesh == NULL) - return; - - u16 mc = mesh->getMeshBufferCount(); - for(u16 j=0; jgetMeshBuffer(j); - video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices(); - u16 vc = buf->getVertexCount(); - for(u16 i=0; isetPosition(m_position); - m_node->setPosition(pos_translator.vect_show); - - v3f rot = m_node->getRotation(); - rot.Y = 180.0 - m_yaw + 90.0; - m_node->setRotation(rot); -} - -void Oerkki1CAO::step(float dtime, ClientEnvironment *env) -{ - pos_translator.translate(dtime); - updateNodePos(); - - LocalPlayer *player = env->getLocalPlayer(); - assert(player); - - v3f playerpos = player->getPosition(); - v2f playerpos_2d(playerpos.X,playerpos.Z); - v2f objectpos_2d(m_position.X,m_position.Z); - - if(fabs(m_position.Y - playerpos.Y) < 3.0*BS && - objectpos_2d.getDistanceFrom(playerpos_2d) < 1.0*BS) - { - if(m_attack_interval.step(dtime, 0.5)) - { - env->damageLocalPlayer(2); - } - } -} - -void Oerkki1CAO::processMessage(const std::string &data) -{ - //dstream<<"Oerkki1CAO: Got message"< 1.0) - anim_time = anim_time_counter; - else - anim_time = anim_time * 0.9 + anim_time_counter * 0.1; - anim_time_counter = 0; - anim_counter = 0; - } - - void translate(f32 dtime) - { - anim_time_counter = anim_time_counter + dtime; - anim_counter = anim_counter + dtime; - v3f vect_move = vect_aim - vect_old; - f32 moveratio = 1.0; - if(anim_time > 0.001) - moveratio = anim_time_counter / anim_time; - // Move a bit less than should, to avoid oscillation - moveratio = moveratio * 0.8; - if(moveratio > 1.5) - moveratio = 1.5; - vect_show = vect_old + vect_move * moveratio; - } -}; - class ClientEnvironment; class ClientActiveObject : public ActiveObject @@ -153,164 +95,5 @@ struct DistanceSortedActiveObject } }; -/* - TestCAO -*/ - -class TestCAO : public ClientActiveObject -{ -public: - TestCAO(); - virtual ~TestCAO(); - - u8 getType() const - { - return ACTIVEOBJECT_TYPE_TEST; - } - - static ClientActiveObject* create(); - - void addToScene(scene::ISceneManager *smgr); - void removeFromScene(); - void updateLight(u8 light_at_pos); - v3s16 getLightPosition(); - void updateNodePos(); - - void step(float dtime, ClientEnvironment *env); - - void processMessage(const std::string &data); - -private: - scene::IMeshSceneNode *m_node; - v3f m_position; -}; - -/* - ItemCAO -*/ - -class ItemCAO : public ClientActiveObject -{ -public: - ItemCAO(); - virtual ~ItemCAO(); - - u8 getType() const - { - return ACTIVEOBJECT_TYPE_ITEM; - } - - static ClientActiveObject* create(); - - void addToScene(scene::ISceneManager *smgr); - void removeFromScene(); - void updateLight(u8 light_at_pos); - v3s16 getLightPosition(); - void updateNodePos(); - - void step(float dtime, ClientEnvironment *env); - - void processMessage(const std::string &data); - - void initialize(const std::string &data); - - core::aabbox3d* getSelectionBox() - {return &m_selection_box;} - v3f getPosition() - {return m_position;} - -private: - core::aabbox3d m_selection_box; - scene::IMeshSceneNode *m_node; - v3f m_position; - std::string m_inventorystring; -}; - -/* - RatCAO -*/ - -class RatCAO : public ClientActiveObject -{ -public: - RatCAO(); - virtual ~RatCAO(); - - u8 getType() const - { - return ACTIVEOBJECT_TYPE_RAT; - } - - static ClientActiveObject* create(); - - void addToScene(scene::ISceneManager *smgr); - void removeFromScene(); - void updateLight(u8 light_at_pos); - v3s16 getLightPosition(); - void updateNodePos(); - - void step(float dtime, ClientEnvironment *env); - - void processMessage(const std::string &data); - - void initialize(const std::string &data); - - core::aabbox3d* getSelectionBox() - {return &m_selection_box;} - v3f getPosition() - {return m_position;} - -private: - core::aabbox3d m_selection_box; - scene::IMeshSceneNode *m_node; - v3f m_position; - float m_yaw; - SmoothTranslator pos_translator; -}; - -/* - Oerkki1CAO -*/ - -class Oerkki1CAO : public ClientActiveObject -{ -public: - Oerkki1CAO(); - virtual ~Oerkki1CAO(); - - u8 getType() const - { - return ACTIVEOBJECT_TYPE_OERKKI1; - } - - static ClientActiveObject* create(); - - void addToScene(scene::ISceneManager *smgr); - void removeFromScene(); - void updateLight(u8 light_at_pos); - v3s16 getLightPosition(); - void updateNodePos(); - - void step(float dtime, ClientEnvironment *env); - - void processMessage(const std::string &data); - - void initialize(const std::string &data); - - core::aabbox3d* getSelectionBox() - {return &m_selection_box;} - v3f getPosition() - {return pos_translator.vect_show;} - //{return m_position;} - -private: - IntervalLimiter m_attack_interval; - core::aabbox3d m_selection_box; - scene::IMeshSceneNode *m_node; - v3f m_position; - float m_yaw; - SmoothTranslator pos_translator; -}; - #endif diff --git a/src/clientserver.h b/src/clientserver.h index 7972762c0..35484fe76 100644 --- a/src/clientserver.h +++ b/src/clientserver.h @@ -37,7 +37,7 @@ enum ToClientCommand [0] u16 TOSERVER_INIT [2] u8 deployed version [3] v3s16 player's position + v3f(0,BS/2,0) floatToInt'd - ([4] u64 map seed (new as of 2011-02-27)) + [12] u64 map seed (new as of 2011-02-27) NOTE: The position in here is deprecated; position is explicitly sent afterwards diff --git a/src/collision.cpp b/src/collision.cpp index 63186a84a..01d546284 100644 --- a/src/collision.cpp +++ b/src/collision.cpp @@ -182,4 +182,58 @@ collisionMoveResult collisionMoveSimple(Map *map, f32 pos_max_d, return result; } +collisionMoveResult collisionMovePrecise(Map *map, f32 pos_max_d, + const core::aabbox3d &box_0, + f32 dtime, v3f &pos_f, v3f &speed_f) +{ + collisionMoveResult final_result; + + // Maximum time increment (for collision detection etc) + // time = distance / speed + f32 dtime_max_increment = pos_max_d / speed_f.getLength(); + + // Maximum time increment is 10ms or lower + if(dtime_max_increment > 0.01) + dtime_max_increment = 0.01; + + // Don't allow overly huge dtime + if(dtime > 2.0) + dtime = 2.0; + + f32 dtime_downcount = dtime; + + u32 loopcount = 0; + do + { + loopcount++; + + f32 dtime_part; + if(dtime_downcount > dtime_max_increment) + { + dtime_part = dtime_max_increment; + dtime_downcount -= dtime_part; + } + else + { + dtime_part = dtime_downcount; + /* + Setting this to 0 (no -=dtime_part) disables an infinite loop + when dtime_part is so small that dtime_downcount -= dtime_part + does nothing + */ + dtime_downcount = 0; + } + + collisionMoveResult result = collisionMoveSimple(map, pos_max_d, + box_0, dtime_part, pos_f, speed_f); + + if(result.touching_ground) + final_result.touching_ground = true; + } + while(dtime_downcount > 0.001); + + + return final_result; +} + diff --git a/src/collision.h b/src/collision.h index 9c913c6a9..6d167bb7b 100644 --- a/src/collision.h +++ b/src/collision.h @@ -33,10 +33,15 @@ struct collisionMoveResult {} }; +// Moves using a single iteration; speed should not exceed pos_max_d/dtime collisionMoveResult collisionMoveSimple(Map *map, f32 pos_max_d, const core::aabbox3d &box_0, f32 dtime, v3f &pos_f, v3f &speed_f); -//{return collisionMoveResult();} + +// Moves using as many iterations as needed +collisionMoveResult collisionMovePrecise(Map *map, f32 pos_max_d, + const core::aabbox3d &box_0, + f32 dtime, v3f &pos_f, v3f &speed_f); enum CollisionType { diff --git a/src/content_cao.cpp b/src/content_cao.cpp new file mode 100644 index 000000000..ab20f1a95 --- /dev/null +++ b/src/content_cao.cpp @@ -0,0 +1,753 @@ +/* +Minetest-c55 +Copyright (C) 2010-2011 celeron55, Perttu Ahola + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "content_cao.h" +#include "tile.h" +#include "environment.h" + +/* + TestCAO +*/ + +// Prototype +TestCAO proto_TestCAO; + +TestCAO::TestCAO(): + ClientActiveObject(0), + m_node(NULL), + m_position(v3f(0,10*BS,0)) +{ + ClientActiveObject::registerType(getType(), create); +} + +TestCAO::~TestCAO() +{ +} + +ClientActiveObject* TestCAO::create() +{ + return new TestCAO(); +} + +void TestCAO::addToScene(scene::ISceneManager *smgr) +{ + if(m_node != NULL) + return; + + video::IVideoDriver* driver = smgr->getVideoDriver(); + + scene::SMesh *mesh = new scene::SMesh(); + scene::IMeshBuffer *buf = new scene::SMeshBuffer(); + video::SColor c(255,255,255,255); + video::S3DVertex vertices[4] = + { + video::S3DVertex(-BS/2,-BS/4,0, 0,0,0, c, 0,1), + video::S3DVertex(BS/2,-BS/4,0, 0,0,0, c, 1,1), + video::S3DVertex(BS/2,BS/4,0, 0,0,0, c, 1,0), + video::S3DVertex(-BS/2,BS/4,0, 0,0,0, c, 0,0), + }; + u16 indices[] = {0,1,2,2,3,0}; + buf->append(vertices, 4, indices, 6); + // Set material + buf->getMaterial().setFlag(video::EMF_LIGHTING, false); + buf->getMaterial().setFlag(video::EMF_BACK_FACE_CULLING, false); + buf->getMaterial().setTexture + (0, driver->getTexture(getTexturePath("rat.png").c_str())); + buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false); + buf->getMaterial().setFlag(video::EMF_FOG_ENABLE, true); + buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; + // Add to mesh + mesh->addMeshBuffer(buf); + buf->drop(); + m_node = smgr->addMeshSceneNode(mesh, NULL); + mesh->drop(); + updateNodePos(); +} + +void TestCAO::removeFromScene() +{ + if(m_node == NULL) + return; + + m_node->remove(); + m_node = NULL; +} + +void TestCAO::updateLight(u8 light_at_pos) +{ +} + +v3s16 TestCAO::getLightPosition() +{ + return floatToInt(m_position, BS); +} + +void TestCAO::updateNodePos() +{ + if(m_node == NULL) + return; + + m_node->setPosition(m_position); + //m_node->setRotation(v3f(0, 45, 0)); +} + +void TestCAO::step(float dtime, ClientEnvironment *env) +{ + if(m_node) + { + v3f rot = m_node->getRotation(); + //dstream<<"dtime="<>cmd; + if(cmd == 0) + { + v3f newpos; + is>>newpos.X; + is>>newpos.Y; + is>>newpos.Z; + m_position = newpos; + updateNodePos(); + } +} + +/* + ItemCAO +*/ + +#include "inventory.h" + +// Prototype +ItemCAO proto_ItemCAO; + +ItemCAO::ItemCAO(): + ClientActiveObject(0), + m_selection_box(-BS/3.,0.0,-BS/3., BS/3.,BS*2./3.,BS/3.), + m_node(NULL), + m_position(v3f(0,10*BS,0)) +{ + ClientActiveObject::registerType(getType(), create); +} + +ItemCAO::~ItemCAO() +{ +} + +ClientActiveObject* ItemCAO::create() +{ + return new ItemCAO(); +} + +void ItemCAO::addToScene(scene::ISceneManager *smgr) +{ + if(m_node != NULL) + return; + + video::IVideoDriver* driver = smgr->getVideoDriver(); + + scene::SMesh *mesh = new scene::SMesh(); + scene::IMeshBuffer *buf = new scene::SMeshBuffer(); + video::SColor c(255,255,255,255); + video::S3DVertex vertices[4] = + { + /*video::S3DVertex(-BS/2,-BS/4,0, 0,0,0, c, 0,1), + video::S3DVertex(BS/2,-BS/4,0, 0,0,0, c, 1,1), + video::S3DVertex(BS/2,BS/4,0, 0,0,0, c, 1,0), + video::S3DVertex(-BS/2,BS/4,0, 0,0,0, c, 0,0),*/ + video::S3DVertex(BS/3.,0,0, 0,0,0, c, 0,1), + video::S3DVertex(-BS/3.,0,0, 0,0,0, c, 1,1), + video::S3DVertex(-BS/3.,0+BS*2./3.,0, 0,0,0, c, 1,0), + video::S3DVertex(BS/3.,0+BS*2./3.,0, 0,0,0, c, 0,0), + }; + u16 indices[] = {0,1,2,2,3,0}; + buf->append(vertices, 4, indices, 6); + // Set material + buf->getMaterial().setFlag(video::EMF_LIGHTING, false); + buf->getMaterial().setFlag(video::EMF_BACK_FACE_CULLING, false); + //buf->getMaterial().setTexture(0, NULL); + // Initialize with the stick texture + buf->getMaterial().setTexture + (0, driver->getTexture(getTexturePath("stick.png").c_str())); + buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false); + buf->getMaterial().setFlag(video::EMF_FOG_ENABLE, true); + buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; + // Add to mesh + mesh->addMeshBuffer(buf); + buf->drop(); + m_node = smgr->addMeshSceneNode(mesh, NULL); + mesh->drop(); + // Set it to use the materials of the meshbuffers directly. + // This is needed for changing the texture in the future + m_node->setReadOnlyMaterials(true); + updateNodePos(); +} + +void ItemCAO::removeFromScene() +{ + if(m_node == NULL) + return; + + m_node->remove(); + m_node = NULL; +} + +void ItemCAO::updateLight(u8 light_at_pos) +{ + if(m_node == NULL) + return; + + u8 li = decode_light(light_at_pos); + video::SColor color(255,li,li,li); + + scene::IMesh *mesh = m_node->getMesh(); + if(mesh == NULL) + return; + + u16 mc = mesh->getMeshBufferCount(); + for(u16 j=0; jgetMeshBuffer(j); + video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices(); + u16 vc = buf->getVertexCount(); + for(u16 i=0; isetPosition(m_position); +} + +void ItemCAO::step(float dtime, ClientEnvironment *env) +{ + if(m_node) + { + /*v3f rot = m_node->getRotation(); + rot.Y += dtime * 120; + m_node->setRotation(rot);*/ + LocalPlayer *player = env->getLocalPlayer(); + assert(player); + v3f rot = m_node->getRotation(); + rot.Y = 180.0 - (player->getYaw()); + m_node->setRotation(rot); + } +} + +void ItemCAO::processMessage(const std::string &data) +{ + dstream<<"ItemCAO: Got message"<getMesh(); + + if(mesh == NULL) + return; + + scene::IMeshBuffer *buf = mesh->getMeshBuffer(0); + + if(buf == NULL) + return; + + // Create an inventory item to see what is its image + std::istringstream is(m_inventorystring, std::ios_base::binary); + video::ITexture *texture = NULL; + try{ + InventoryItem *item = NULL; + item = InventoryItem::deSerialize(is); + dstream<<__FUNCTION_NAME<<": m_inventorystring=\"" + < item="<getImage(); + delete item; + } + } + catch(SerializationError &e) + { + dstream<<"WARNING: "<<__FUNCTION_NAME + <<": error deSerializing inventorystring \"" + <getMaterial().setTexture(0, texture); + +} + +/* + RatCAO +*/ + +#include "inventory.h" + +// Prototype +RatCAO proto_RatCAO; + +RatCAO::RatCAO(): + ClientActiveObject(0), + m_selection_box(-BS/3.,0.0,-BS/3., BS/3.,BS/2.,BS/3.), + m_node(NULL), + m_position(v3f(0,10*BS,0)), + m_yaw(0) +{ + ClientActiveObject::registerType(getType(), create); +} + +RatCAO::~RatCAO() +{ +} + +ClientActiveObject* RatCAO::create() +{ + return new RatCAO(); +} + +void RatCAO::addToScene(scene::ISceneManager *smgr) +{ + if(m_node != NULL) + return; + + video::IVideoDriver* driver = smgr->getVideoDriver(); + + scene::SMesh *mesh = new scene::SMesh(); + scene::IMeshBuffer *buf = new scene::SMeshBuffer(); + video::SColor c(255,255,255,255); + video::S3DVertex vertices[4] = + { + video::S3DVertex(-BS/2,0,0, 0,0,0, c, 0,1), + video::S3DVertex(BS/2,0,0, 0,0,0, c, 1,1), + video::S3DVertex(BS/2,BS/2,0, 0,0,0, c, 1,0), + video::S3DVertex(-BS/2,BS/2,0, 0,0,0, c, 0,0), + }; + u16 indices[] = {0,1,2,2,3,0}; + buf->append(vertices, 4, indices, 6); + // Set material + buf->getMaterial().setFlag(video::EMF_LIGHTING, false); + buf->getMaterial().setFlag(video::EMF_BACK_FACE_CULLING, false); + //buf->getMaterial().setTexture(0, NULL); + buf->getMaterial().setTexture + (0, driver->getTexture(getTexturePath("rat.png").c_str())); + buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false); + buf->getMaterial().setFlag(video::EMF_FOG_ENABLE, true); + buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; + // Add to mesh + mesh->addMeshBuffer(buf); + buf->drop(); + m_node = smgr->addMeshSceneNode(mesh, NULL); + mesh->drop(); + // Set it to use the materials of the meshbuffers directly. + // This is needed for changing the texture in the future + m_node->setReadOnlyMaterials(true); + updateNodePos(); +} + +void RatCAO::removeFromScene() +{ + if(m_node == NULL) + return; + + m_node->remove(); + m_node = NULL; +} + +void RatCAO::updateLight(u8 light_at_pos) +{ + if(m_node == NULL) + return; + + u8 li = decode_light(light_at_pos); + video::SColor color(255,li,li,li); + + scene::IMesh *mesh = m_node->getMesh(); + if(mesh == NULL) + return; + + u16 mc = mesh->getMeshBufferCount(); + for(u16 j=0; jgetMeshBuffer(j); + video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices(); + u16 vc = buf->getVertexCount(); + for(u16 i=0; isetPosition(m_position); + m_node->setPosition(pos_translator.vect_show); + + v3f rot = m_node->getRotation(); + rot.Y = 180.0 - m_yaw; + m_node->setRotation(rot); +} + +void RatCAO::step(float dtime, ClientEnvironment *env) +{ + pos_translator.translate(dtime); + updateNodePos(); +} + +void RatCAO::processMessage(const std::string &data) +{ + //dstream<<"RatCAO: Got message"<getVideoDriver(); + + scene::SMesh *mesh = new scene::SMesh(); + scene::IMeshBuffer *buf = new scene::SMeshBuffer(); + video::SColor c(255,255,255,255); + video::S3DVertex vertices[4] = + { + video::S3DVertex(-BS/2-BS,0,0, 0,0,0, c, 0,1), + video::S3DVertex(BS/2+BS,0,0, 0,0,0, c, 1,1), + video::S3DVertex(BS/2+BS,BS*2,0, 0,0,0, c, 1,0), + video::S3DVertex(-BS/2-BS,BS*2,0, 0,0,0, c, 0,0), + }; + u16 indices[] = {0,1,2,2,3,0}; + buf->append(vertices, 4, indices, 6); + // Set material + buf->getMaterial().setFlag(video::EMF_LIGHTING, false); + buf->getMaterial().setFlag(video::EMF_BACK_FACE_CULLING, false); + //buf->getMaterial().setTexture(0, NULL); + buf->getMaterial().setTexture + (0, driver->getTexture(getTexturePath("oerkki1.png").c_str())); + buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false); + buf->getMaterial().setFlag(video::EMF_FOG_ENABLE, true); + buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; + // Add to mesh + mesh->addMeshBuffer(buf); + buf->drop(); + m_node = smgr->addMeshSceneNode(mesh, NULL); + mesh->drop(); + // Set it to use the materials of the meshbuffers directly. + // This is needed for changing the texture in the future + m_node->setReadOnlyMaterials(true); + updateNodePos(); +} + +void Oerkki1CAO::removeFromScene() +{ + if(m_node == NULL) + return; + + m_node->remove(); + m_node = NULL; +} + +void Oerkki1CAO::updateLight(u8 light_at_pos) +{ + if(m_node == NULL) + return; + + if(light_at_pos <= 2) + { + m_node->setVisible(false); + return; + } + + m_node->setVisible(true); + + u8 li = decode_light(light_at_pos); + video::SColor color(255,li,li,li); + + scene::IMesh *mesh = m_node->getMesh(); + if(mesh == NULL) + return; + + u16 mc = mesh->getMeshBufferCount(); + for(u16 j=0; jgetMeshBuffer(j); + video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices(); + u16 vc = buf->getVertexCount(); + for(u16 i=0; isetPosition(m_position); + m_node->setPosition(pos_translator.vect_show); + + v3f rot = m_node->getRotation(); + rot.Y = 180.0 - m_yaw + 90.0; + m_node->setRotation(rot); +} + +void Oerkki1CAO::step(float dtime, ClientEnvironment *env) +{ + pos_translator.translate(dtime); + updateNodePos(); + + LocalPlayer *player = env->getLocalPlayer(); + assert(player); + + v3f playerpos = player->getPosition(); + v2f playerpos_2d(playerpos.X,playerpos.Z); + v2f objectpos_2d(m_position.X,m_position.Z); + + if(fabs(m_position.Y - playerpos.Y) < 3.0*BS && + objectpos_2d.getDistanceFrom(playerpos_2d) < 1.5*BS) + { + if(m_attack_interval.step(dtime, 0.5)) + { + env->damageLocalPlayer(2); + } + } + + if(m_damage_visual_timer > 0) + { + if(!m_damage_texture_enabled) + { + // Enable damage texture + if(m_node) + { + video::IVideoDriver* driver = + m_node->getSceneManager()->getVideoDriver(); + + scene::IMesh *mesh = m_node->getMesh(); + if(mesh == NULL) + return; + + u16 mc = mesh->getMeshBufferCount(); + for(u16 j=0; jgetMeshBuffer(j); + buf->getMaterial().setTexture(0, driver->getTexture( + getTexturePath("oerkki1_damaged.png").c_str())); + } + } + m_damage_texture_enabled = true; + } + m_damage_visual_timer -= dtime; + } + else + { + if(m_damage_texture_enabled) + { + // Disable damage texture + if(m_node) + { + video::IVideoDriver* driver = + m_node->getSceneManager()->getVideoDriver(); + + scene::IMesh *mesh = m_node->getMesh(); + if(mesh == NULL) + return; + + u16 mc = mesh->getMeshBufferCount(); + for(u16 j=0; jgetMeshBuffer(j); + buf->getMaterial().setTexture(0, driver->getTexture( + getTexturePath("oerkki1.png").c_str())); + } + } + m_damage_texture_enabled = false; + } + } +} + +void Oerkki1CAO::processMessage(const std::string &data) +{ + //dstream<<"Oerkki1CAO: Got message"< + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#ifndef CONTENT_CAO_HEADER +#define CONTENT_CAO_HEADER + +#include "clientobject.h" +#include "content_object.h" +#include "utility.h" // For IntervalLimiter + +/* + SmoothTranslator +*/ + +struct SmoothTranslator +{ + v3f vect_old; + f32 anim_counter; + f32 anim_time; + f32 anim_time_counter; + v3f vect_show; + v3f vect_aim; + + SmoothTranslator(): + vect_old(0,0,0), + anim_counter(0), + anim_time(0), + anim_time_counter(0), + vect_show(0,0,0), + vect_aim(0,0,0) + {} + + void init(v3f vect) + { + vect_old = vect; + vect_show = vect; + vect_aim = vect; + } + + void update(v3f vect_new) + { + vect_old = vect_show; + vect_aim = vect_new; + if(anim_time < 0.001 || anim_time > 1.0) + anim_time = anim_time_counter; + else + anim_time = anim_time * 0.9 + anim_time_counter * 0.1; + anim_time_counter = 0; + anim_counter = 0; + } + + void translate(f32 dtime) + { + anim_time_counter = anim_time_counter + dtime; + anim_counter = anim_counter + dtime; + v3f vect_move = vect_aim - vect_old; + f32 moveratio = 1.0; + if(anim_time > 0.001) + moveratio = anim_time_counter / anim_time; + // Move a bit less than should, to avoid oscillation + moveratio = moveratio * 0.8; + if(moveratio > 1.5) + moveratio = 1.5; + vect_show = vect_old + vect_move * moveratio; + } +}; + + +/* + TestCAO +*/ + +class TestCAO : public ClientActiveObject +{ +public: + TestCAO(); + virtual ~TestCAO(); + + u8 getType() const + { + return ACTIVEOBJECT_TYPE_TEST; + } + + static ClientActiveObject* create(); + + void addToScene(scene::ISceneManager *smgr); + void removeFromScene(); + void updateLight(u8 light_at_pos); + v3s16 getLightPosition(); + void updateNodePos(); + + void step(float dtime, ClientEnvironment *env); + + void processMessage(const std::string &data); + +private: + scene::IMeshSceneNode *m_node; + v3f m_position; +}; + +/* + ItemCAO +*/ + +class ItemCAO : public ClientActiveObject +{ +public: + ItemCAO(); + virtual ~ItemCAO(); + + u8 getType() const + { + return ACTIVEOBJECT_TYPE_ITEM; + } + + static ClientActiveObject* create(); + + void addToScene(scene::ISceneManager *smgr); + void removeFromScene(); + void updateLight(u8 light_at_pos); + v3s16 getLightPosition(); + void updateNodePos(); + + void step(float dtime, ClientEnvironment *env); + + void processMessage(const std::string &data); + + void initialize(const std::string &data); + + core::aabbox3d* getSelectionBox() + {return &m_selection_box;} + v3f getPosition() + {return m_position;} + +private: + core::aabbox3d m_selection_box; + scene::IMeshSceneNode *m_node; + v3f m_position; + std::string m_inventorystring; +}; + +/* + RatCAO +*/ + +class RatCAO : public ClientActiveObject +{ +public: + RatCAO(); + virtual ~RatCAO(); + + u8 getType() const + { + return ACTIVEOBJECT_TYPE_RAT; + } + + static ClientActiveObject* create(); + + void addToScene(scene::ISceneManager *smgr); + void removeFromScene(); + void updateLight(u8 light_at_pos); + v3s16 getLightPosition(); + void updateNodePos(); + + void step(float dtime, ClientEnvironment *env); + + void processMessage(const std::string &data); + + void initialize(const std::string &data); + + core::aabbox3d* getSelectionBox() + {return &m_selection_box;} + v3f getPosition() + {return m_position;} + +private: + core::aabbox3d m_selection_box; + scene::IMeshSceneNode *m_node; + v3f m_position; + float m_yaw; + SmoothTranslator pos_translator; +}; + +/* + Oerkki1CAO +*/ + +class Oerkki1CAO : public ClientActiveObject +{ +public: + Oerkki1CAO(); + virtual ~Oerkki1CAO(); + + u8 getType() const + { + return ACTIVEOBJECT_TYPE_OERKKI1; + } + + static ClientActiveObject* create(); + + void addToScene(scene::ISceneManager *smgr); + void removeFromScene(); + void updateLight(u8 light_at_pos); + v3s16 getLightPosition(); + void updateNodePos(); + + void step(float dtime, ClientEnvironment *env); + + void processMessage(const std::string &data); + + void initialize(const std::string &data); + + core::aabbox3d* getSelectionBox() + {return &m_selection_box;} + v3f getPosition() + {return pos_translator.vect_show;} + //{return m_position;} + +private: + IntervalLimiter m_attack_interval; + core::aabbox3d m_selection_box; + scene::IMeshSceneNode *m_node; + v3f m_position; + float m_yaw; + SmoothTranslator pos_translator; + float m_damage_visual_timer; + bool m_damage_texture_enabled; +}; + + +#endif + diff --git a/src/content_inventory.cpp b/src/content_inventory.cpp index 357c8ef26..7d995cb5f 100644 --- a/src/content_inventory.cpp +++ b/src/content_inventory.cpp @@ -19,8 +19,9 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "content_inventory.h" #include "inventory.h" -#include "serverobject.h" #include "content_mapnode.h" +//#include "serverobject.h" +#include "content_sao.h" bool item_material_is_cookable(u8 content) { diff --git a/src/content_mapblock.cpp b/src/content_mapblock.cpp index 9ef0599d1..4c28fe3c6 100644 --- a/src/content_mapblock.cpp +++ b/src/content_mapblock.cpp @@ -272,7 +272,7 @@ void mapblock_mesh_generate_special(MeshMakeData *data, /* Signs on walls */ - if(n.d == CONTENT_SIGN_WALL) + else if(n.d == CONTENT_SIGN_WALL) { u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio)); video::SColor c(255,l,l,l); diff --git a/src/content_mapnode.cpp b/src/content_mapnode.cpp index 38356599f..79e10fd61 100644 --- a/src/content_mapnode.cpp +++ b/src/content_mapnode.cpp @@ -224,7 +224,6 @@ void content_mapnode_init() // Deprecated i = CONTENT_COALSTONE; f = &content_features(i); - //f->translate_to = new MapNode(CONTENT_STONE, MINERAL_COAL); f->setAllTextures("stone.png^mineral_coal.png"); f->is_ground_content = true; setStoneLikeDiggingProperties(f->digging_properties, 1.5); diff --git a/src/content_object.h b/src/content_object.h new file mode 100644 index 000000000..ecabd8a38 --- /dev/null +++ b/src/content_object.h @@ -0,0 +1,29 @@ +/* +Minetest-c55 +Copyright (C) 2010-2011 celeron55, Perttu Ahola + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#ifndef CONTENT_OBJECT_HEADER +#define CONTENT_OBJECT_HEADER + +#define ACTIVEOBJECT_TYPE_TEST 1 +#define ACTIVEOBJECT_TYPE_ITEM 2 +#define ACTIVEOBJECT_TYPE_RAT 3 +#define ACTIVEOBJECT_TYPE_OERKKI1 4 + +#endif + diff --git a/src/content_sao.cpp b/src/content_sao.cpp new file mode 100644 index 000000000..fc6f208a0 --- /dev/null +++ b/src/content_sao.cpp @@ -0,0 +1,694 @@ +/* +Minetest-c55 +Copyright (C) 2010-2011 celeron55, Perttu Ahola + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "content_sao.h" +#include "collision.h" +#include "environment.h" + +/* + TestSAO +*/ + +// Prototype +TestSAO proto_TestSAO(NULL, 0, v3f(0,0,0)); + +TestSAO::TestSAO(ServerEnvironment *env, u16 id, v3f pos): + ServerActiveObject(env, id, pos), + m_timer1(0), + m_age(0) +{ + ServerActiveObject::registerType(getType(), create); +} + +ServerActiveObject* TestSAO::create(ServerEnvironment *env, u16 id, v3f pos, + const std::string &data) +{ + return new TestSAO(env, id, pos); +} + +void TestSAO::step(float dtime, bool send_recommended) +{ + m_age += dtime; + if(m_age > 10) + { + m_removed = true; + return; + } + + m_base_position.Y += dtime * BS * 2; + if(m_base_position.Y > 8*BS) + m_base_position.Y = 2*BS; + + if(send_recommended == false) + return; + + m_timer1 -= dtime; + if(m_timer1 < 0.0) + { + m_timer1 += 0.125; + //dstream<<"TestSAO: id="< box(-BS/3.,0.0,-BS/3., BS/3.,BS*2./3.,BS/3.); + collisionMoveResult moveresult; + // Apply gravity + m_speed_f += v3f(0, -dtime*9.81*BS, 0); + // Maximum movement without glitches + f32 pos_max_d = BS*0.25; + // Limit speed + if(m_speed_f.getLength()*dtime > pos_max_d) + m_speed_f *= pos_max_d / (m_speed_f.getLength()*dtime); + v3f pos_f = getBasePosition(); + v3f pos_f_old = pos_f; + moveresult = collisionMoveSimple(&m_env->getMap(), pos_max_d, + box, dtime, pos_f, m_speed_f); + + if(send_recommended == false) + return; + + if(pos_f.getDistanceFrom(m_last_sent_position) > 0.05*BS) + { + setBasePosition(pos_f); + m_last_sent_position = pos_f; + + std::ostringstream os(std::ios::binary); + char buf[6]; + // command (0 = update position) + buf[0] = 0; + os.write(buf, 1); + // pos + writeS32((u8*)buf, m_base_position.X*1000); + os.write(buf, 4); + writeS32((u8*)buf, m_base_position.Y*1000); + os.write(buf, 4); + writeS32((u8*)buf, m_base_position.Z*1000); + os.write(buf, 4); + // create message and add to list + ActiveObjectMessage aom(getId(), false, os.str()); + m_messages_out.push_back(aom); + } +} + +std::string ItemSAO::getClientInitializationData() +{ + std::ostringstream os(std::ios::binary); + char buf[6]; + // version + buf[0] = 0; + os.write(buf, 1); + // pos + writeS32((u8*)buf, m_base_position.X*1000); + os.write(buf, 4); + writeS32((u8*)buf, m_base_position.Y*1000); + os.write(buf, 4); + writeS32((u8*)buf, m_base_position.Z*1000); + os.write(buf, 4); + // inventorystring + os< item="< 60) + { + // Die + m_removed = true; + return; + }*/ + + // Apply gravity + m_speed_f.Y -= dtime*9.81*BS; + + /* + Move around if some player is close + */ + bool player_is_close = false; + // Check connected players + core::list players = m_env->getPlayers(true); + core::list::Iterator i; + for(i = players.begin(); + i != players.end(); i++) + { + Player *player = *i; + v3f playerpos = player->getPosition(); + if(m_base_position.getDistanceFrom(playerpos) < BS*10.0) + { + player_is_close = true; + break; + } + } + + m_is_active = player_is_close; + + if(player_is_close == false) + { + m_speed_f.X = 0; + m_speed_f.Z = 0; + } + else + { + // Move around + v3f dir(cos(m_yaw/180*PI),0,sin(m_yaw/180*PI)); + f32 speed = 2*BS; + m_speed_f.X = speed * dir.X; + m_speed_f.Z = speed * dir.Z; + + if(m_touching_ground && (m_oldpos - m_base_position).getLength() + < dtime*speed/2) + { + m_counter1 -= dtime; + if(m_counter1 < 0.0) + { + m_counter1 += 1.0; + m_speed_f.Y = 5.0*BS; + } + } + + { + m_counter2 -= dtime; + if(m_counter2 < 0.0) + { + m_counter2 += (float)(myrand()%100)/100*3.0; + m_yaw += ((float)(myrand()%200)-100)/100*180; + m_yaw = wrapDegrees(m_yaw); + } + } + } + + m_oldpos = m_base_position; + + /* + Move it, with collision detection + */ + + core::aabbox3d box(-BS/3.,0.0,-BS/3., BS/3.,BS*2./3.,BS/3.); + collisionMoveResult moveresult; + // Maximum movement without glitches + f32 pos_max_d = BS*0.25; + // Limit speed + if(m_speed_f.getLength()*dtime > pos_max_d) + m_speed_f *= pos_max_d / (m_speed_f.getLength()*dtime); + v3f pos_f = getBasePosition(); + v3f pos_f_old = pos_f; + moveresult = collisionMoveSimple(&m_env->getMap(), pos_max_d, + box, dtime, pos_f, m_speed_f); + m_touching_ground = moveresult.touching_ground; + + setBasePosition(pos_f); + + if(send_recommended == false) + return; + + if(pos_f.getDistanceFrom(m_last_sent_position) > 0.05*BS) + { + m_last_sent_position = pos_f; + + std::ostringstream os(std::ios::binary); + // command (0 = update position) + writeU8(os, 0); + // pos + writeV3F1000(os, m_base_position); + // yaw + writeF1000(os, m_yaw); + // create message and add to list + ActiveObjectMessage aom(getId(), false, os.str()); + m_messages_out.push_back(aom); + } +} + +std::string RatSAO::getClientInitializationData() +{ + std::ostringstream os(std::ios::binary); + // version + writeU8(os, 0); + // pos + writeV3F1000(os, m_base_position); + return os.str(); +} + +std::string RatSAO::getStaticData() +{ + //dstream<<__FUNCTION_NAME< max_increase) + dl = max_increase; + + v3f d = d_wanted.normalize() * dl; + + speed.X += d.X; + speed.Z += d.Z; + speed.Y = target_speed.Y; +} + +// Prototype +Oerkki1SAO proto_Oerkki1SAO(NULL, 0, v3f(0,0,0)); + +Oerkki1SAO::Oerkki1SAO(ServerEnvironment *env, u16 id, v3f pos): + ServerActiveObject(env, id, pos), + m_is_active(false), + m_speed_f(0,0,0) +{ + ServerActiveObject::registerType(getType(), create); + + m_oldpos = v3f(0,0,0); + m_last_sent_position = v3f(0,0,0); + m_yaw = 0; + m_counter1 = 0; + m_counter2 = 0; + m_age = 0; + m_touching_ground = false; + m_hp = 20; + m_after_jump_timer = 0; +} + +ServerActiveObject* Oerkki1SAO::create(ServerEnvironment *env, u16 id, v3f pos, + const std::string &data) +{ + std::istringstream is(data, std::ios::binary); + // read version + u8 version = readU8(is); + // read hp + u8 hp = readU8(is); + // check if version is supported + if(version != 0) + return NULL; + Oerkki1SAO *o = new Oerkki1SAO(env, id, pos); + o->m_hp = hp; + return o; +} + +void Oerkki1SAO::step(float dtime, bool send_recommended) +{ + assert(m_env); + + if(m_is_active == false) + { + if(m_inactive_interval.step(dtime, 0.5)==false) + return; + } + + /* + The AI + */ + + m_age += dtime; + if(m_age > 120) + { + // Die + m_removed = true; + return; + } + + m_after_jump_timer -= dtime; + + v3f old_speed = m_speed_f; + + // Apply gravity + m_speed_f.Y -= dtime*9.81*BS; + + /* + Move around if some player is close + */ + bool player_is_close = false; + bool player_is_too_close = false; + v3f near_player_pos; + // Check connected players + core::list players = m_env->getPlayers(true); + core::list::Iterator i; + for(i = players.begin(); + i != players.end(); i++) + { + Player *player = *i; + v3f playerpos = player->getPosition(); + f32 dist = m_base_position.getDistanceFrom(playerpos); + if(dist < BS*1.45) + { + player_is_too_close = true; + near_player_pos = playerpos; + break; + } + else if(dist < BS*15.0) + { + player_is_close = true; + near_player_pos = playerpos; + } + } + + m_is_active = player_is_close; + + v3f target_speed = m_speed_f; + + if(!player_is_close) + { + target_speed = v3f(0,0,0); + } + else + { + // Move around + + v3f ndir = near_player_pos - m_base_position; + ndir.Y = 0; + ndir.normalize(); + + f32 nyaw = 180./PI*atan2(ndir.Z,ndir.X); + if(nyaw < m_yaw - 180) + nyaw += 360; + else if(nyaw > m_yaw + 180) + nyaw -= 360; + m_yaw = 0.95*m_yaw + 0.05*nyaw; + m_yaw = wrapDegrees(m_yaw); + + f32 speed = 2*BS; + + if((m_touching_ground || m_after_jump_timer > 0.0) + && !player_is_too_close) + { + v3f dir(cos(m_yaw/180*PI),0,sin(m_yaw/180*PI)); + target_speed.X = speed * dir.X; + target_speed.Z = speed * dir.Z; + } + + if(m_touching_ground && (m_oldpos - m_base_position).getLength() + < dtime*speed/2) + { + m_counter1 -= dtime; + if(m_counter1 < 0.0) + { + m_counter1 += 0.2; + // Jump + target_speed.Y = 5.0*BS; + m_after_jump_timer = 1.0; + } + } + + { + m_counter2 -= dtime; + if(m_counter2 < 0.0) + { + m_counter2 += (float)(myrand()%100)/100*3.0; + //m_yaw += ((float)(myrand()%200)-100)/100*180; + m_yaw += ((float)(myrand()%200)-100)/100*90; + m_yaw = wrapDegrees(m_yaw); + } + } + } + + if((m_speed_f - target_speed).getLength() > BS*4 || player_is_too_close) + accelerate_xz(m_speed_f, target_speed, dtime*BS*8); + else + accelerate_xz(m_speed_f, target_speed, dtime*BS*4); + + m_oldpos = m_base_position; + + /* + Move it, with collision detection + */ + + core::aabbox3d box(-BS/3.,0.0,-BS/3., BS/3.,BS*5./3.,BS/3.); + collisionMoveResult moveresult; + // Maximum movement without glitches + f32 pos_max_d = BS*0.25; + /*// Limit speed + if(m_speed_f.getLength()*dtime > pos_max_d) + m_speed_f *= pos_max_d / (m_speed_f.getLength()*dtime);*/ + v3f pos_f = getBasePosition(); + v3f pos_f_old = pos_f; + moveresult = collisionMovePrecise(&m_env->getMap(), pos_max_d, + box, dtime, pos_f, m_speed_f); + m_touching_ground = moveresult.touching_ground; + + // Do collision damage + float tolerance = BS*12; + float factor = BS*0.5; + v3f speed_diff = old_speed - m_speed_f; + // Increase effect in X and Z + speed_diff.X *= 2; + speed_diff.Z *= 2; + float vel = speed_diff.getLength(); + if(vel > tolerance) + { + f32 damage_f = (vel - tolerance)/BS*factor; + u16 damage = (u16)(damage_f+0.5); + doDamage(damage); + } + + setBasePosition(pos_f); + + if(send_recommended == false && m_speed_f.getLength() < 3.0*BS) + return; + + if(pos_f.getDistanceFrom(m_last_sent_position) > 0.05*BS) + { + m_last_sent_position = pos_f; + + std::ostringstream os(std::ios::binary); + // command (0 = update position) + writeU8(os, 0); + // pos + writeV3F1000(os, m_base_position); + // yaw + writeF1000(os, m_yaw); + // create message and add to list + ActiveObjectMessage aom(getId(), false, os.str()); + m_messages_out.push_back(aom); + } +} + +std::string Oerkki1SAO::getClientInitializationData() +{ + std::ostringstream os(std::ios::binary); + // version + writeU8(os, 0); + // pos + writeV3F1000(os, m_base_position); + return os.str(); +} + +std::string Oerkki1SAO::getStaticData() +{ + //dstream<<__FUNCTION_NAME< + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#ifndef CONTENT_SAO_HEADER +#define CONTENT_SAO_HEADER + +#include "serverobject.h" +#include "content_object.h" + +class TestSAO : public ServerActiveObject +{ +public: + TestSAO(ServerEnvironment *env, u16 id, v3f pos); + u8 getType() const + {return ACTIVEOBJECT_TYPE_TEST;} + static ServerActiveObject* create(ServerEnvironment *env, u16 id, v3f pos, + const std::string &data); + void step(float dtime, bool send_recommended); +private: + float m_timer1; + float m_age; +}; + +class ItemSAO : public ServerActiveObject +{ +public: + ItemSAO(ServerEnvironment *env, u16 id, v3f pos, + const std::string inventorystring); + u8 getType() const + {return ACTIVEOBJECT_TYPE_ITEM;} + static ServerActiveObject* create(ServerEnvironment *env, u16 id, v3f pos, + const std::string &data); + void step(float dtime, bool send_recommended); + std::string getClientInitializationData(); + std::string getStaticData(); + InventoryItem* createInventoryItem(); + InventoryItem* createPickedUpItem(){return createInventoryItem();} +private: + std::string m_inventorystring; + v3f m_speed_f; + v3f m_last_sent_position; + IntervalLimiter m_move_interval; +}; + +class RatSAO : public ServerActiveObject +{ +public: + RatSAO(ServerEnvironment *env, u16 id, v3f pos); + u8 getType() const + {return ACTIVEOBJECT_TYPE_RAT;} + static ServerActiveObject* create(ServerEnvironment *env, u16 id, v3f pos, + const std::string &data); + void step(float dtime, bool send_recommended); + std::string getClientInitializationData(); + std::string getStaticData(); + InventoryItem* createPickedUpItem(); +private: + bool m_is_active; + IntervalLimiter m_inactive_interval; + v3f m_speed_f; + v3f m_oldpos; + v3f m_last_sent_position; + float m_yaw; + float m_counter1; + float m_counter2; + float m_age; + bool m_touching_ground; +}; + +class Oerkki1SAO : public ServerActiveObject +{ +public: + Oerkki1SAO(ServerEnvironment *env, u16 id, v3f pos); + u8 getType() const + {return ACTIVEOBJECT_TYPE_OERKKI1;} + static ServerActiveObject* create(ServerEnvironment *env, u16 id, v3f pos, + const std::string &data); + void step(float dtime, bool send_recommended); + std::string getClientInitializationData(); + std::string getStaticData(); + InventoryItem* createPickedUpItem(){return NULL;} + u16 punch(const std::string &toolname, v3f dir); +private: + void doDamage(u16 d); + + bool m_is_active; + IntervalLimiter m_inactive_interval; + v3f m_speed_f; + v3f m_oldpos; + v3f m_last_sent_position; + float m_yaw; + float m_counter1; + float m_counter2; + float m_age; + bool m_touching_ground; + u8 m_hp; + float m_after_jump_timer; +}; + + +#endif + diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index ac1983ed1..cbc78ad3f 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -42,6 +42,8 @@ void set_default_settings() g_settings.setDefault("keymap_rangeselect", "KEY_KEY_R"); g_settings.setDefault("keymap_freemove", "KEY_KEY_K"); g_settings.setDefault("keymap_fastmove", "KEY_KEY_J"); + g_settings.setDefault("keymap_frametime_graph", "KEY_F1"); + g_settings.setDefault("keymap_screenshot", "KEY_F12"); // Some (temporary) keys for debugging g_settings.setDefault("keymap_special1", "KEY_KEY_E"); g_settings.setDefault("keymap_print_debug_stacks", "KEY_KEY_P"); @@ -54,7 +56,7 @@ void set_default_settings() g_settings.setDefault("screenH", "600"); g_settings.setDefault("address", ""); g_settings.setDefault("random_input", "false"); - g_settings.setDefault("client_delete_unused_sectors_timeout", "1200"); + g_settings.setDefault("client_unload_unused_data_timeout", "600"); g_settings.setDefault("enable_fog", "true"); g_settings.setDefault("new_style_water", "false"); g_settings.setDefault("new_style_leaves", "true"); @@ -72,6 +74,7 @@ void set_default_settings() g_settings.setDefault("farmesh_distance", "40"); g_settings.setDefault("enable_clouds", "true"); g_settings.setDefault("invisible_stone", "false"); + g_settings.setDefault("screenshot_path", "."); // Server stuff g_settings.setDefault("enable_experimental", "false"); @@ -81,17 +84,19 @@ void set_default_settings() g_settings.setDefault("default_password", ""); g_settings.setDefault("default_privs", "build, shout"); g_settings.setDefault("profiler_print_interval", "0"); + g_settings.setDefault("enable_mapgen_debug_info", "false"); g_settings.setDefault("objectdata_interval", "0.2"); g_settings.setDefault("active_object_range", "2"); //g_settings.setDefault("max_simultaneous_block_sends_per_client", "1"); + // This causes frametime jitter on client side, or does it? g_settings.setDefault("max_simultaneous_block_sends_per_client", "2"); g_settings.setDefault("max_simultaneous_block_sends_server_total", "8"); g_settings.setDefault("max_block_send_distance", "8"); g_settings.setDefault("max_block_generate_distance", "8"); g_settings.setDefault("time_send_interval", "20"); g_settings.setDefault("time_speed", "96"); - g_settings.setDefault("server_unload_unused_sectors_timeout", "60"); + g_settings.setDefault("server_unload_unused_data_timeout", "60"); g_settings.setDefault("server_map_save_interval", "60"); g_settings.setDefault("full_block_send_enable_min_time_from_building", "2.0"); //g_settings.setDefault("dungeon_rarity", "0.025"); diff --git a/src/environment.cpp b/src/environment.cpp index e2c704259..0dab54213 100644 --- a/src/environment.cpp +++ b/src/environment.cpp @@ -22,7 +22,9 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "porting.h" #include "collision.h" #include "content_mapnode.h" - +#include "mapblock.h" +#include "serverobject.h" +#include "content_sao.h" Environment::Environment(): m_time_of_day(9000) @@ -658,14 +660,6 @@ void ServerEnvironment::step(float dtime) m_game_time_fraction_counter -= (float)inc_i; } - /* - Let map update it's timers - */ - { - //TimeTaker timer("Server m_map->timerUpdate()"); - m_map->timerUpdate(dtime); - } - /* Handle players */ @@ -867,6 +861,9 @@ void ServerEnvironment::step(float dtime) MapBlock *block = m_map->getBlockNoCreateNoEx(p); if(block==NULL) continue; + + // Reset block usage timer + block->resetUsageTimer(); // Set current time as timestamp block->setTimestampNoChangedFlag(m_game_time); @@ -986,8 +983,14 @@ void ServerEnvironment::step(float dtime) // Don't step if is to be removed or stored statically if(obj->m_removed || obj->m_pending_deactivation) continue; - // Step object, putting messages directly to the queue - obj->step(dtime, m_active_object_messages, send_recommended); + // Step object + obj->step(dtime, send_recommended); + // Read messages from object + while(obj->m_messages_out.size() > 0) + { + m_active_object_messages.push_back( + obj->m_messages_out.pop_front()); + } } } @@ -1242,7 +1245,7 @@ u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object, block->setChangedFlag(); } else{ - dstream<<"WARNING: Server: Could not find a block for " + dstream<<"WARNING: ServerEnv: Could not find a block for " <<"storing newly added static active object"<getType(), objectpos, staticdata); // Add to the block where the object is located in v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS)); - MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos); + // Get or generate the block + MapBlock *block = m_map->emergeBlock(blockpos); + + /*MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos); + if(block == NULL) + { + // Block not found. Is the old block still ok? + if(oldblock) + block = oldblock; + // Load from disk or generate + else + block = m_map->emergeBlock(blockpos); + }*/ + if(block) { block->m_static_objects.insert(0, s_obj); @@ -1422,17 +1438,9 @@ void ServerEnvironment::deactivateFarObjects(bool force_delete) obj->m_static_exists = true; obj->m_static_block = block->getPos(); } - // If not possible, add back to previous block - else if(oldblock) - { - oldblock->m_static_objects.insert(0, s_obj); - oldblock->setChangedFlag(); - obj->m_static_exists = true; - obj->m_static_block = oldblock->getPos(); - } else{ - dstream<<"WARNING: Server: Could not find a block for " - <<"storing static object"<m_static_exists = false; continue; } @@ -1526,11 +1534,6 @@ void ClientEnvironment::step(float dtime) bool free_move = g_settings.getBool("free_move"); bool footprints = g_settings.getBool("footprints"); - { - //TimeTaker timer("Client m_map->timerUpdate()"); - m_map->timerUpdate(dtime); - } - // Get local player LocalPlayer *lplayer = getLocalPlayer(); assert(lplayer); @@ -1728,17 +1731,21 @@ void ClientEnvironment::step(float dtime) ClientActiveObject* obj = i.getNode()->getValue(); // Step object obj->step(dtime, this); - // Update lighting - //u8 light = LIGHT_MAX; - u8 light = 0; - try{ - // Get node at head - v3s16 p = obj->getLightPosition(); - MapNode n = m_map->getNode(p); - light = n.getLightBlend(getDayNightRatio()); + + if(m_active_object_light_update_interval.step(dtime, 0.21)) + { + // Update lighting + //u8 light = LIGHT_MAX; + u8 light = 0; + try{ + // Get node at head + v3s16 p = obj->getLightPosition(); + MapNode n = m_map->getNode(p); + light = n.getLightBlend(getDayNightRatio()); + } + catch(InvalidPositionException &e) {} + obj->updateLight(light); } - catch(InvalidPositionException &e) {} - obj->updateLight(light); } } @@ -1926,6 +1933,22 @@ ClientEnvEvent ClientEnvironment::getClientEvent() return m_client_event_queue.pop_front(); } +void ClientEnvironment::drawPostFx(video::IVideoDriver* driver, v3f camera_pos) +{ + /*LocalPlayer *player = getLocalPlayer(); + assert(player); + v3f pos_f = player->getPosition() + v3f(0,BS*1.625,0);*/ + v3f pos_f = camera_pos; + v3s16 p_nodes = floatToInt(pos_f, BS); + MapNode n = m_map->getNodeNoEx(p_nodes); + if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE) + { + v2u32 ss = driver->getScreenSize(); + core::rect rect(0,0, ss.X, ss.Y); + driver->draw2DRectangle(video::SColor(64, 100, 100, 200), rect); + } +} + #endif // #ifndef SERVER diff --git a/src/environment.h b/src/environment.h index eac69d222..b6767858a 100644 --- a/src/environment.h +++ b/src/environment.h @@ -36,6 +36,11 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "map.h" #include #include "utility.h" +#include "activeobject.h" + +class Server; +class ActiveBlockModifier; +class ServerActiveObject; class Environment { @@ -118,11 +123,6 @@ private: This is not thread-safe. Server uses an environment mutex. */ -#include "serverobject.h" - -class Server; -class ActiveBlockModifier; - class ServerEnvironment : public Environment { public: @@ -406,12 +406,16 @@ public: // Get event from queue. CEE_NONE is returned if queue is empty. ClientEnvEvent getClientEvent(); + + // Post effects + void drawPostFx(video::IVideoDriver* driver, v3f camera_pos); private: ClientMap *m_map; scene::ISceneManager *m_smgr; core::map m_active_objects; Queue m_client_event_queue; + IntervalLimiter m_active_object_light_update_interval; }; #endif diff --git a/src/farmesh.cpp b/src/farmesh.cpp index a35983729..2cd922434 100644 --- a/src/farmesh.cpp +++ b/src/farmesh.cpp @@ -280,7 +280,8 @@ void FarMesh::render() if(h_avg < WATER_LEVEL*BS && h_max < (WATER_LEVEL+5)*BS) { //c = video::SColor(255,59,86,146); - c = video::SColor(255,82,120,204); + //c = video::SColor(255,82,120,204); + c = video::SColor(255,74,105,170); /*// Set to water level for(u32 i=0; i<4; i++) diff --git a/src/game.cpp b/src/game.cpp index 367abebe1..d77b45da5 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -30,8 +30,12 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "clouds.h" #include "keycode.h" #include "farmesh.h" +#include "mapblock.h" -// TODO: Move content-aware stuff to separate file +/* + TODO: Move content-aware stuff to separate file by adding properties + and virtual interfaces +*/ #include "content_mapnode.h" #include "content_nodemeta.h" @@ -672,6 +676,34 @@ void update_skybox(video::IVideoDriver* driver, } } +/* + Draws a screen with a single text on it. + Text will be removed when the screen is drawn the next time. +*/ +/*gui::IGUIStaticText **/ +void draw_load_screen(const std::wstring &text, + video::IVideoDriver* driver, gui::IGUIFont* font) +{ + v2u32 screensize = driver->getScreenSize(); + const wchar_t *loadingtext = text.c_str(); + core::vector2d textsize_u = font->getDimension(loadingtext); + core::vector2d textsize(textsize_u.X,textsize_u.Y); + core::vector2d center(screensize.X/2, screensize.Y/2); + core::rect textrect(center - textsize/2, center + textsize/2); + + gui::IGUIStaticText *guitext = guienv->addStaticText( + loadingtext, textrect, false, false); + guitext->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT); + + driver->beginScene(true, true, video::SColor(255,0,0,0)); + guienv->drawAll(); + driver->endScene(); + + guitext->remove(); + + //return guitext; +} + void the_game( bool &kill, bool random_input, @@ -688,13 +720,18 @@ void the_game( { video::IVideoDriver* driver = device->getVideoDriver(); scene::ISceneManager* smgr = device->getSceneManager(); + + // Calculate text height using the font + u32 text_height = font->getDimension(L"Random test string").Height; v2u32 screensize(0,0); v2u32 last_screensize(0,0); screensize = driver->getScreenSize(); const s32 hotbar_itemcount = 8; - const s32 hotbar_imagesize = 36; + //const s32 hotbar_imagesize = 36; + //const s32 hotbar_imagesize = 64; + s32 hotbar_imagesize = 48; // The color of the sky @@ -705,20 +742,10 @@ void the_game( /* Draw "Loading" screen */ - const wchar_t *loadingtext = L"Loading and connecting..."; - u32 text_height = font->getDimension(loadingtext).Height; - core::vector2d center(screensize.X/2, screensize.Y/2); - core::vector2d textsize(300, text_height); - core::rect textrect(center - textsize/2, center + textsize/2); - - gui::IGUIStaticText *gui_loadingtext = guienv->addStaticText( - loadingtext, textrect, false, false); - gui_loadingtext->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT); - - driver->beginScene(true, true, video::SColor(255,0,0,0)); - guienv->drawAll(); - driver->endScene(); + /*gui::IGUIStaticText *gui_loadingtext = */ + //draw_load_screen(L"Loading and connecting...", driver, font); + draw_load_screen(L"Loading...", driver, font); /* Create server. @@ -726,6 +753,7 @@ void the_game( */ SharedPtr server; if(address == ""){ + draw_load_screen(L"Creating server...", driver, font); std::cout<start(port); @@ -735,9 +763,11 @@ void the_game( Create client */ + draw_load_screen(L"Creating client...", driver, font); std::cout<remove(); + //gui_loadingtext->remove(); return; } @@ -784,11 +814,17 @@ void the_game( { break; } + + std::wostringstream ss; + ss<beginScene(true, true, video::SColor(255,0,0,0)); guienv->drawAll(); - driver->endScene(); + driver->endScene();*/ // Update client and server @@ -818,7 +854,7 @@ void the_game( error_message = L"Connection timed out."; std::cout<remove(); + //gui_loadingtext->remove(); return; } @@ -880,7 +916,7 @@ void the_game( Move into game */ - gui_loadingtext->remove(); + //gui_loadingtext->remove(); /* Add some gui stuff @@ -973,6 +1009,8 @@ void the_game( while(device->run() && kill == false) { + //std::cerr<<"frame"<disconnect_requested) { g_gamecallback->disconnect_requested = false; @@ -998,6 +1036,14 @@ void the_game( screensize = driver->getScreenSize(); v2s32 displaycenter(screensize.X/2,screensize.Y/2); //bool screensize_changed = screensize != last_screensize; + + // Resize hotbar + if(screensize.Y <= 600) + hotbar_imagesize = 32; + else if(screensize.Y <= 1024) + hotbar_imagesize = 48; + else + hotbar_imagesize = 64; // Hilight boxes collected during the loop and displayed core::list< core::aabbox3d > hilightboxes; @@ -1090,7 +1136,7 @@ void the_game( */ static f32 dtime_avg1 = 0.0; - dtime_avg1 = dtime_avg1 * 0.98 + dtime * 0.02; + dtime_avg1 = dtime_avg1 * 0.96 + dtime * 0.04; f32 dtime_jitter1 = dtime - dtime_avg1; static f32 dtime_jitter1_max_sample = 0.0; @@ -1254,6 +1300,38 @@ void the_game( chat_lines.push_back(ChatLine(L"fast_move enabled")); } } + else if(input->wasKeyDown(getKeySetting("keymap_frametime_graph"))) + { + if(g_settings.getBool("frametime_graph")) + { + g_settings.set("frametime_graph","false"); + chat_lines.push_back(ChatLine(L"frametime_graph disabled")); + } + else + { + g_settings.set("frametime_graph","true"); + chat_lines.push_back(ChatLine(L"frametime_graph enabled")); + } + } + else if(input->wasKeyDown(getKeySetting("keymap_screenshot"))) + { + irr::video::IImage* const image = driver->createScreenShot(); + if (image) { + irr::c8 filename[256]; + snprintf(filename, 256, "%s/screenshot_%u.png", + g_settings.get("screenshot_path").c_str(), + device->getTimer()->getRealTime()); + if (driver->writeImageToFile(image, filename)) { + std::wstringstream sstr; + sstr<<"Saved screenshot to '"<drop(); + } + } // Item selection with mouse wheel { @@ -2194,6 +2272,13 @@ void the_game( core::rect(0,0,screensize.X,screensize.Y), NULL); } + + /* + Environment post fx + */ + { + client.getEnv()->drawPostFx(driver, camera_position); + } /* End scene @@ -2237,15 +2322,12 @@ void the_game( generator and other stuff quits */ { - const wchar_t *shuttingdowntext = L"Shutting down stuff..."; - gui::IGUIStaticText *gui_shuttingdowntext = guienv->addStaticText( - shuttingdowntext, textrect, false, false); - gui_shuttingdowntext->setTextAlignment(gui::EGUIA_CENTER, - gui::EGUIA_UPPERLEFT); - driver->beginScene(true, true, video::SColor(255,0,0,0)); + /*gui::IGUIStaticText *gui_shuttingdowntext = */ + draw_load_screen(L"Shutting down stuff...", driver, font); + /*driver->beginScene(true, true, video::SColor(255,0,0,0)); guienv->drawAll(); driver->endScene(); - gui_shuttingdowntext->remove(); + gui_shuttingdowntext->remove();*/ } } diff --git a/src/inventory.cpp b/src/inventory.cpp index 0c76ed845..fec51a759 100644 --- a/src/inventory.cpp +++ b/src/inventory.cpp @@ -30,6 +30,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "serverobject.h" #include "content_mapnode.h" #include "content_inventory.h" +#include "content_sao.h" /* InventoryItem diff --git a/src/main.cpp b/src/main.cpp index 41da310f4..698c5fc71 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -27,6 +27,33 @@ NOTE: Global locale is now set at initialization NOTE: If VBO (EHM_STATIC) is used, remember to explicitly free the hardware buffer (it is not freed automatically) +NOTE: A random to-do list saved here as documentation: +A list of "active blocks" in which stuff happens. (+=done) + + Add a never-resetted game timer to the server + + Add a timestamp value to blocks + + The simple rule: All blocks near some player are "active" + - Do stuff in real time in active blocks + + Handle objects + - Grow grass, delete leaves without a tree + - Spawn some mobs based on some rules + - Transform cobble to mossy cobble near water + - Run a custom script + - ...And all kinds of other dynamic stuff + + Keep track of when a block becomes active and becomes inactive + + When a block goes inactive: + + Store objects statically to block + + Store timer value as the timestamp + + When a block goes active: + + Create active objects out of static objects + - Simulate the results of what would have happened if it would have + been active for all the time + - Grow a lot of grass and so on + + Initially it is fine to send information about every active object + to every player. Eventually it should be modified to only send info + about the nearest ones. + + This was left to be done by the old system and it sends only the + nearest ones. + Old, wild and random suggestions that probably won't be done: ------------------------------------------------------------- @@ -73,9 +100,6 @@ SUGG: Make the amount of blocks sending to client and the total SUGG: Meshes of blocks could be split into 6 meshes facing into different directions and then only those drawn that need to be -SUGG: Calculate lighting per vertex to get a lighting effect like in - bartwe's game - SUGG: Background music based on cellular automata? http://www.earslap.com/projectslab/otomata @@ -90,6 +114,8 @@ SUGG: Make a system for pregenerating quick information for mapblocks, so or even generated. SUGG: Erosion simulation at map generation time + - This might be plausible if larger areas of map were pregenerated + without lighting (which is slow) - Simulate water flows, which would carve out dirt fast and then turn stone into gravel and sand and relocate it. - How about relocating minerals, too? Coal and gold in @@ -100,6 +126,16 @@ SUGG: Erosion simulation at map generation time - Simulate rock falling from cliffs when water has removed enough solid rock from the bottom +SUGG: For non-mapgen FarMesh: Add a per-sector database to store surface + stuff as simple flags/values + - Light? + - A building? + And at some point make the server send this data to the client too, + instead of referring to the noise functions + - Ground height + - Surface ground type + - Trees? + Gaming ideas: ------------- @@ -173,12 +209,13 @@ SUGG: Make fetching sector's blocks more efficient when rendering sectors that have very large amounts of blocks (on client) - Is this necessary at all? -TODO: Flowing water animation - SUGG: Draw cubes in inventory directly with 3D drawing commands, so that animating them is easier. SUGG: Option for enabling proper alpha channel for textures + +TODO: Flowing water animation + TODO: A setting for enabling bilinear filtering for textures TODO: Better control of draw_control.wanted_max_blocks @@ -193,6 +230,12 @@ TODO: Artificial (night) light could be more yellow colored than sunlight. SUGG: Somehow make the night less colorful +TODO: Occlusion culling + - At the same time, move some of the renderMap() block choosing code + to the same place as where the new culling happens. + - Shoot some rays per frame and when ready, make a new list of + blocks for usage of renderMap and give it a new pointer to it. + Configuration: -------------- @@ -231,6 +274,7 @@ FIXME: Server sometimes goes into some infinite PeerNotFoundException loop * Fix the problem with the server constantly saving one or a few blocks? List the first saved block, maybe it explains. - It is probably caused by oscillating water + - TODO: Investigate if this still happens (this is a very old one) * Make a small history check to transformLiquids to detect and log continuous oscillations, in such detail that they can be fixed. @@ -238,44 +282,12 @@ FIXME: The new optimized map sending doesn't sometimes send enough blocks from big caves and such FIXME: Block send distance configuration does not take effect for some reason -TODO: Map saving should be done by EmergeThread - -SUGG: Map unloading based on sector reference is not very good, it keeps - unnecessary stuff in memory. I guess. Investigate this. - -TODO: When block is placed and it has param_type==CPT_FACEDIR_SIMPLE, set - the direction accordingly. - Environment: ------------ -TODO: A list of "active blocks" in which stuff happens. (+=done) - + Add a never-resetted game timer to the server - + Add a timestamp value to blocks - + The simple rule: All blocks near some player are "active" - - Do stuff in real time in active blocks - + Handle objects - TODO: Make proper hooks in here - - Grow grass, delete leaves without a tree - - Spawn some mobs based on some rules - - Transform cobble to mossy cobble near water - - Run a custom script - - ...And all kinds of other dynamic stuff - + Keep track of when a block becomes active and becomes inactive - + When a block goes inactive: - + Store objects statically to block - + Store timer value as the timestamp - + When a block goes active: - + Create active objects out of static objects - TODO: Make proper hooks in here - - Simulate the results of what would have happened if it would have - been active for all the time - - Grow a lot of grass and so on - + Initially it is fine to send information about every active object - to every player. Eventually it should be modified to only send info - about the nearest ones. - + This was left to be done by the old system and it sends only the - nearest ones. +TODO: Add proper hooks to when adding and removing active blocks + +TODO: Finish the ActiveBlockModifier stuff and use it for something Objects: -------- @@ -287,6 +299,7 @@ TODO: Get rid of MapBlockObjects and use only ActiveObjects SUGG: MovingObject::move and Player::move are basically the same. combine them. + - NOTE: This is a bit tricky because player has the sneaking ability - NOTE: Player::move is more up-to-date. - NOTE: There is a simple move implementation now in collision.{h,cpp} - NOTE: MovingObject will be deleted (MapBlockObject) @@ -305,59 +318,25 @@ TODO: Mineral and ground material properties TODO: Flowing water to actually contain flow direction information - There is a space for this - it just has to be implemented. -SUGG: Try out the notch way of generating maps, that is, make bunches - of low-res 3d noise and interpolate linearly. - -Mapgen v2 (the current one): -* Possibly add some kind of erosion and other stuff -* Better water generation (spread it to underwater caverns but don't - fill dungeons that don't touch big water masses) -* When generating a chunk and the neighboring chunk doesn't have mud - and stuff yet and the ground is fairly flat, the mud will flow to - the other chunk making nasty straight walls when the other chunk - is generated. Fix it. Maybe just a special case if the ground is - flat? -* Consider not updating this one and make a good mainly block-based - generator - -SUGG: Make two "modified states", one that forces the block to be saved at - the next save event, and one that makes the block to be saved at exit - time. - -TODO: Add a not_fully_generated flag to MapBlock, which would be set for - blocks that contain eg. trees from neighboring generations but haven't - been generated itself. This is required for the future generator. +TODO: Consider smoothening cave floors after generating them Misc. stuff: ------------ -- Make sure server handles removing grass when a block is placed (etc) - - The client should not do it by itself -- Block cube placement around player's head -- Protocol version field -- Consider getting some textures from cisoun's texture pack - - Ask from Cisoun -- Make sure the fence implementation and data format is good - - Think about using same bits for material for fences and doors, for - example -- Finish the ActiveBlockModifier stuff and use it for something -- Move mineral to param2, increment map serialization version, add conversion - -TODO: Add a per-sector database to store surface stuff as simple flags/values - - Light? - - A building? - And at some point make the server send this data to the client too, - instead of referring to the noise functions - - Ground height - - Surface ground type - - Trees? +TODO: Make sure server handles removing grass when a block is placed (etc) + - The client should not do it by itself + - NOTE: I think nobody does it currently... +TODO: Block cube placement around player's head +TODO: Protocol version field +TODO: Think about using same bits for material for fences and doors, for + example +TODO: Move mineral to param2, increment map serialization version, add + conversion TODO: Restart irrlicht completely when coming back to main menu from game. - This gets rid of everything that is stored in irrlicht's caches. TODO: Merge bahamada's audio stuff (clean patch available) -TODO: Merge spongie's chest/furnace direction (by hand) - TODO: Merge key configuration menu (no clean patch available) Making it more portable: @@ -375,9 +354,6 @@ Stuff to do after release: Doing currently: ---------------- -TODO: Use MapBlock::resetUsageTimer() in appropriate places - (on client and server) - ====================================================================== */ @@ -406,16 +382,12 @@ TODO: Use MapBlock::resetUsageTimer() in appropriate places #include #include -//#include #include #include "main.h" #include "common_irrlicht.h" #include "debug.h" -//#include "map.h" -//#include "player.h" #include "test.h" #include "server.h" -//#include "client.h" #include "constants.h" #include "porting.h" #include "gettime.h" @@ -424,11 +396,10 @@ TODO: Use MapBlock::resetUsageTimer() in appropriate places #include "config.h" #include "guiMainMenu.h" #include "mineral.h" -//#include "noise.h" -//#include "tile.h" #include "materials.h" #include "game.h" #include "keycode.h" +#include "tile.h" // This makes textures ITextureSource *g_texturesource = NULL; diff --git a/src/main.h b/src/main.h index 450525c26..b2dee1494 100644 --- a/src/main.h +++ b/src/main.h @@ -25,7 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc., extern Settings g_settings; // This makes and maps textures -#include "tile.h" +class ITextureSource; extern ITextureSource *g_texturesource; // Global profiler diff --git a/src/map.cpp b/src/map.cpp index d3e898357..10e1302b1 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -18,18 +18,16 @@ with this program; if not, write to the Free Software Foundation, Inc., */ #include "map.h" +#include "mapsector.h" +#include "mapblock.h" #include "main.h" -#include "jmutexautolock.h" #include "client.h" #include "filesys.h" #include "utility.h" #include "voxel.h" #include "porting.h" -#include "mineral.h" -#include "noise.h" -#include "serverobject.h" -#include "content_mapnode.h" #include "mapgen.h" +#include "nodemetadata.h" extern "C" { #include "sqlite3.h" @@ -122,42 +120,23 @@ MapSector * Map::getSectorNoGenerate(v2s16 p) return sector; } -MapBlock * Map::getBlockNoCreate(v3s16 p3d) -{ - v2s16 p2d(p3d.X, p3d.Z); - MapSector * sector = getSectorNoGenerate(p2d); - - MapBlock *block = sector->getBlockNoCreate(p3d.Y); - - return block; -} - MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d) { - try - { - v2s16 p2d(p3d.X, p3d.Z); - MapSector * sector = getSectorNoGenerate(p2d); - MapBlock *block = sector->getBlockNoCreate(p3d.Y); - return block; - } - catch(InvalidPositionException &e) - { + v2s16 p2d(p3d.X, p3d.Z); + MapSector * sector = getSectorNoGenerateNoEx(p2d); + if(sector == NULL) return NULL; - } + MapBlock *block = sector->getBlockNoCreateNoEx(p3d.Y); + return block; } -/*MapBlock * Map::getBlockCreate(v3s16 p3d) -{ - v2s16 p2d(p3d.X, p3d.Z); - MapSector * sector = getSectorCreate(p2d); - assert(sector); - MapBlock *block = sector->getBlockNoCreate(p3d.Y); - if(block) - return block; - block = sector->createBlankBlock(p3d.Y); +MapBlock * Map::getBlockNoCreate(v3s16 p3d) +{ + MapBlock *block = getBlockNoCreateNoEx(p3d); + if(block == NULL) + throw InvalidPositionException(); return block; -}*/ +} bool Map::isNodeUnderground(v3s16 p) { @@ -172,6 +151,45 @@ bool Map::isNodeUnderground(v3s16 p) } } +bool Map::isValidPosition(v3s16 p) +{ + v3s16 blockpos = getNodeBlockPos(p); + MapBlock *block = getBlockNoCreate(blockpos); + return (block != NULL); +} + +// Returns a CONTENT_IGNORE node if not found +MapNode Map::getNodeNoEx(v3s16 p) +{ + v3s16 blockpos = getNodeBlockPos(p); + MapBlock *block = getBlockNoCreateNoEx(blockpos); + if(block == NULL) + return MapNode(CONTENT_IGNORE); + v3s16 relpos = p - blockpos*MAP_BLOCKSIZE; + return block->getNodeNoCheck(relpos); +} + +// throws InvalidPositionException if not found +MapNode Map::getNode(v3s16 p) +{ + v3s16 blockpos = getNodeBlockPos(p); + MapBlock *block = getBlockNoCreateNoEx(blockpos); + if(block == NULL) + throw InvalidPositionException(); + v3s16 relpos = p - blockpos*MAP_BLOCKSIZE; + return block->getNodeNoCheck(relpos); +} + +// throws InvalidPositionException if not found +void Map::setNode(v3s16 p, MapNode & n) +{ + v3s16 blockpos = getNodeBlockPos(p); + MapBlock *block = getBlockNoCreate(blockpos); + v3s16 relpos = p - blockpos*MAP_BLOCKSIZE; + block->setNodeNoCheck(relpos, n); +} + + /* Goes recursively through the neighbours of the node. @@ -737,6 +755,25 @@ void Map::updateLighting(enum LightBank bank, } } + + /* + Enable this to disable proper lighting for speeding up map + generation for testing or whatever + */ +#if 0 + //if(g_settings.get("")) + { + core::map::Iterator i; + i = blocks_to_update.getIterator(); + for(; i.atEnd() == false; i++) + { + MapBlock *block = i.getNode()->getValue(); + v3s16 p = block->getPos(); + block->setLightingExpired(false); + } + return; + } +#endif #if 0 { @@ -879,7 +916,7 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n, { } -#if 1 +#if 0 /* If the new node is solid and there is grass below, change it to mud */ @@ -1357,9 +1394,14 @@ bool Map::dayNightDiffed(v3s16 blockpos) /* Updates usage timers */ -void Map::timerUpdate(float dtime) +void Map::timerUpdate(float dtime, float unload_timeout, + core::list *unloaded_blocks) { - //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out + bool save_before_unloading = (mapType() == MAPTYPE_SERVER); + + core::list sector_deletion_queue; + u32 deleted_blocks_count = 0; + u32 saved_blocks_count = 0; core::map::Iterator si; @@ -1368,48 +1410,85 @@ void Map::timerUpdate(float dtime) { MapSector *sector = si.getNode()->getValue(); + bool all_blocks_deleted = true; + core::list blocks; sector->getBlocks(blocks); for(core::list::Iterator i = blocks.begin(); i != blocks.end(); i++) { - (*i)->incrementUsageTimer(dtime); + MapBlock *block = (*i); + + block->incrementUsageTimer(dtime); + + if(block->getUsageTimer() > unload_timeout) + { + v3s16 p = block->getPos(); + + // Save if modified + if(block->getModified() != MOD_STATE_CLEAN + && save_before_unloading) + { + saveBlock(block); + saved_blocks_count++; + } + + // Delete from memory + sector->deleteBlock(block); + + if(unloaded_blocks) + unloaded_blocks->push_back(p); + + deleted_blocks_count++; + } + else + { + all_blocks_deleted = false; + } } + + if(all_blocks_deleted) + { + sector_deletion_queue.push_back(si.getNode()->getKey()); + } + } + + // Finally delete the empty sectors + deleteSectors(sector_deletion_queue); + + if(deleted_blocks_count != 0) + { + PrintInfo(dstream); // ServerMap/ClientMap: + dstream<<"Unloaded "< &list, bool only_blocks) +void Map::deleteSectors(core::list &list) { core::list::Iterator j; for(j=list.begin(); j!=list.end(); j++) { MapSector *sector = m_sectors[*j]; - if(only_blocks) - { - sector->deleteBlocks(); - } - else - { - /* - If sector is in sector cache, remove it from there - */ - if(m_sector_cache == sector) - { - m_sector_cache = NULL; - } - /* - Remove from map and delete - */ - m_sectors.remove(*j); - delete sector; - } + // If sector is in sector cache, remove it from there + if(m_sector_cache == sector) + m_sector_cache = NULL; + // Remove from map and delete + m_sectors.remove(*j); + delete sector; } } -u32 Map::unloadUnusedData(float timeout, bool only_blocks, +#if 0 +void Map::unloadUnusedData(float timeout, core::list *deleted_blocks) { core::list sector_deletion_queue; + u32 deleted_blocks_count = 0; + u32 saved_blocks_count = 0; core::map::Iterator si = m_sectors.getIterator(); for(; si.atEnd() == false; si++) @@ -1424,15 +1503,18 @@ u32 Map::unloadUnusedData(float timeout, bool only_blocks, i != blocks.end(); i++) { MapBlock *block = (*i); - + if(block->getUsageTimer() > timeout) { // Save if modified if(block->getModified() != MOD_STATE_CLEAN) + { saveBlock(block); - // Unload - sector->removeBlock(block); - delete block; + saved_blocks_count++; + } + // Delete from memory + sector->deleteBlock(block); + deleted_blocks_count++; } else { @@ -1446,37 +1528,16 @@ u32 Map::unloadUnusedData(float timeout, bool only_blocks, } } -#if 0 - core::map::Iterator i = m_sectors.getIterator(); - for(; i.atEnd() == false; i++) - { - MapSector *sector = i.getNode()->getValue(); - /* - Delete sector from memory if it hasn't been used in a long time - */ - if(sector->usage_timer > timeout) - { - sector_deletion_queue.push_back(i.getNode()->getKey()); + deleteSectors(sector_deletion_queue); - if(deleted_blocks != NULL) - { - // Collect positions of blocks of sector - MapSector *sector = i.getNode()->getValue(); - core::list blocks; - sector->getBlocks(blocks); - for(core::list::Iterator i = blocks.begin(); - i != blocks.end(); i++) - { - deleted_blocks->push_back((*i)->getPos()); - } - } - } - } -#endif + dstream<<"Map: Unloaded "< & modified_blocks) */ v3s16 p0 = m_transforming_liquid.pop_front(); - MapNode n0 = getNode(p0); + MapNode n0 = getNodeNoEx(p0); // Don't deal with non-liquids if(content_liquid(n0.d) == false) @@ -1538,13 +1599,10 @@ void Map::transformLiquids(core::map & modified_blocks) }; for(u16 i=0; i<5; i++) { - try - { - bool from_top = (i==0); v3s16 p2 = p0 + dirs_from[i]; - MapNode n2 = getNode(p2); + MapNode n2 = getNodeNoEx(p2); if(content_liquid(n2.d)) { @@ -1593,10 +1651,6 @@ void Map::transformLiquids(core::map & modified_blocks) if(new_liquid_level > new_liquid_level_max) new_liquid_level_max = new_liquid_level; } - - }catch(InvalidPositionException &e) - { - } } //for /* @@ -1645,20 +1699,13 @@ void Map::transformLiquids(core::map & modified_blocks) }; for(u16 i=0; i<6; i++) { - try - { - v3s16 p2 = p0 + dirs[i]; - MapNode n2 = getNode(p2); + MapNode n2 = getNodeNoEx(p2); if(content_flowing_liquid(n2.d)) { m_transforming_liquid.push_back(p2); } - - }catch(InvalidPositionException &e) - { - } } } } @@ -1679,9 +1726,6 @@ void Map::transformLiquids(core::map & modified_blocks) }; for(u16 i=0; i<5; i++) { - try - { - bool to_bottom = (i == 0); // If liquid is at lowest possible height, it's not going @@ -1707,7 +1751,7 @@ void Map::transformLiquids(core::map & modified_blocks) v3s16 p2 = p0 + dirs_to[i]; - MapNode n2 = getNode(p2); + MapNode n2 = getNodeNoEx(p2); //dstream<<"[1] n2.param="<<(int)n2.param< & modified_blocks) // If n2_changed to bottom, don't flow anywhere else if(to_bottom && flowed && !is_source) break; - - }catch(InvalidPositionException &e) - { - } } loopcount++; @@ -1972,7 +2012,6 @@ ServerMap::~ServerMap() { if(m_map_saving_enabled) { - //save(false); // Save only changed parts save(true); dstream<vmanip.print(dstream);*/ @@ -2091,10 +2132,11 @@ MapBlock* ServerMap::finishBlockMake(mapgen::BlockMakeData *data, //TimeTaker timer("finishBlockMake() blitBackAll"); data->vmanip->blitBackAll(&changed_blocks); } -#if 1 - dstream<<"finishBlockMake: changed_blocks.size()=" - < lighting_update_blocks; + // Center block + lighting_update_blocks.insert(block->getPos(), block); + #if 0 + // All modified blocks + for(core::map::Iterator + i = changed_blocks.getIterator(); + i.atEnd() == false; i++) + { + lighting_update_blocks.insert(i.getNode()->getKey(), + i.getNode()->getValue()); + } + #endif + updateLighting(lighting_update_blocks, changed_blocks); + + if(enable_mapgen_debug_info == false) + t.stop(true); // Hide output } -#endif - updateLighting(lighting_update_blocks, changed_blocks); - + /* Add random objects to block */ @@ -2262,6 +2314,8 @@ MapBlock * ServerMap::generateBlock( <<"("< &changed_blocks, - core::map &lighting_invalidated_blocks -) +MapBlock * ServerMap::emergeBlock(v3s16 p, bool allow_generate) { - DSTACKF("%s: p=(%d,%d,%d), only_from_disk=%d", + DSTACKF("%s: p=(%d,%d,%d), allow_generate=%d", __FUNCTION_NAME, - p.X, p.Y, p.Z, only_from_disk); + p.X, p.Y, p.Z, allow_generate); - // This has to be redone or removed - assert(0); + { + MapBlock *block = getBlockNoCreateNoEx(p); + if(block) + return block; + } + + { + MapBlock *block = loadBlock(p); + if(block) + return block; + } + + if(allow_generate) + { + core::map modified_blocks; + MapBlock *block = generateBlock(p, modified_blocks); + if(block) + { + MapEditEvent event; + event.type = MEET_OTHER; + event.p = p; + + // Copy modified_blocks to event + for(core::map::Iterator + i = modified_blocks.getIterator(); + i.atEnd()==false; i++) + { + event.modified_blocks.insert(i.getNode()->getKey(), false); + } + + // Queue event + dispatchEvent(&event); + + return block; + } + } + return NULL; } -#endif #if 0 /* @@ -2877,10 +2965,10 @@ MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load // format. Just go ahead and create the sector. if(fs::PathExists(sectordir)) { - dstream<<"ServerMap::loadSectorMeta(): Sector metafile " + /*dstream<<"ServerMap::loadSectorMeta(): Sector metafile " <getBlockNoCreate(p3d.Y); - } - catch(InvalidPositionException &e) + block = sector->getBlockNoCreateNoEx(p3d.Y); + if(block == NULL) { block = sector->createBlankBlockNoInsert(p3d.Y); created_new = true; @@ -3267,6 +3353,7 @@ MapSector * ClientMap::emergeSector(v2s16 p2d) return sector; } +#if 0 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is) { DSTACK(__FUNCTION_NAME); @@ -3292,6 +3379,7 @@ void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is) sector->deSerialize(is); } +#endif void ClientMap::OnRegisterSceneNode() { @@ -3350,13 +3438,13 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) // Take a fair amount as we will be dropping more out later v3s16 p_blocks_min( - p_nodes_min.X / MAP_BLOCKSIZE - 1, - p_nodes_min.Y / MAP_BLOCKSIZE - 1, - p_nodes_min.Z / MAP_BLOCKSIZE - 1); + p_nodes_min.X / MAP_BLOCKSIZE - 2, + p_nodes_min.Y / MAP_BLOCKSIZE - 2, + p_nodes_min.Z / MAP_BLOCKSIZE - 2); v3s16 p_blocks_max( - p_nodes_max.X / MAP_BLOCKSIZE, - p_nodes_max.Y / MAP_BLOCKSIZE, - p_nodes_max.Z / MAP_BLOCKSIZE); + p_nodes_max.X / MAP_BLOCKSIZE + 1, + p_nodes_max.Y / MAP_BLOCKSIZE + 1, + p_nodes_max.Z / MAP_BLOCKSIZE + 1); u32 vertex_count = 0; @@ -3428,6 +3516,9 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) { continue; } + + // Okay, this block will be drawn. Reset usage timer. + block->resetUsageTimer(); // This is ugly (spherical distance limit?) /*if(m_control.range_all == false && diff --git a/src/map.h b/src/map.h index 86b6b6e18..a8aa8e679 100644 --- a/src/map.h +++ b/src/map.h @@ -21,25 +21,21 @@ with this program; if not, write to the Free Software Foundation, Inc., #define MAP_HEADER #include +#include #include #include -#ifdef _WIN32 - #include - #define sleep_s(x) Sleep((x*1000)) -#else - #include - #define sleep_s(x) sleep(x) -#endif - #include "common_irrlicht.h" #include "mapnode.h" -#include "mapblock.h" -#include "mapsector.h" +#include "mapblock_nodemod.h" #include "constants.h" #include "voxel.h" -#include "mapchunk.h" -#include "nodemetadata.h" + +class MapSector; +class ServerMapSector; +class ClientMapSector; +class MapBlock; +class NodeMetadata; namespace mapgen{ struct BlockMakeData; @@ -61,7 +57,7 @@ enum MapEditEventType{ // Node metadata of block changed (not knowing which node exactly) // p stores block coordinate MEET_BLOCK_NODE_METADATA_CHANGED, - // Anything else + // Anything else (modified_blocks are set unsent) MEET_OTHER }; @@ -104,17 +100,17 @@ public: virtual void onMapEditEvent(MapEditEvent *event) = 0; }; -class Map : public NodeContainer +class Map /*: public NodeContainer*/ { public: Map(std::ostream &dout); virtual ~Map(); - virtual u16 nodeContainerId() const + /*virtual u16 nodeContainerId() const { return NODECONTAINER_ID_MAP; - } + }*/ virtual s32 mapType() const { @@ -155,66 +151,20 @@ public: MapBlock * getBlockNoCreate(v3s16 p); // Returns NULL if not found MapBlock * getBlockNoCreateNoEx(v3s16 p); - // Gets an existing block or creates an empty one - //MapBlock * getBlockCreate(v3s16 p); // Returns InvalidPositionException if not found bool isNodeUnderground(v3s16 p); - // virtual from NodeContainer - bool isValidPosition(v3s16 p) - { - v3s16 blockpos = getNodeBlockPos(p); - MapBlock *blockref; - try{ - blockref = getBlockNoCreate(blockpos); - } - catch(InvalidPositionException &e) - { - return false; - } - return true; - /*v3s16 relpos = p - blockpos*MAP_BLOCKSIZE; - bool is_valid = blockref->isValidPosition(relpos); - return is_valid;*/ - } + bool isValidPosition(v3s16 p); - // virtual from NodeContainer // throws InvalidPositionException if not found - MapNode getNode(v3s16 p) - { - v3s16 blockpos = getNodeBlockPos(p); - MapBlock * blockref = getBlockNoCreate(blockpos); - v3s16 relpos = p - blockpos*MAP_BLOCKSIZE; + MapNode getNode(v3s16 p); - return blockref->getNodeNoCheck(relpos); - } - - // virtual from NodeContainer // throws InvalidPositionException if not found - void setNode(v3s16 p, MapNode & n) - { - v3s16 blockpos = getNodeBlockPos(p); - MapBlock * blockref = getBlockNoCreate(blockpos); - v3s16 relpos = p - blockpos*MAP_BLOCKSIZE; - blockref->setNodeNoCheck(relpos, n); - } + void setNode(v3s16 p, MapNode & n); // Returns a CONTENT_IGNORE node if not found - MapNode getNodeNoEx(v3s16 p) - { - try{ - v3s16 blockpos = getNodeBlockPos(p); - MapBlock * blockref = getBlockNoCreate(blockpos); - v3s16 relpos = p - blockpos*MAP_BLOCKSIZE; - - return blockref->getNodeNoCheck(relpos); - } - catch(InvalidPositionException &e) - { - return MapNode(CONTENT_IGNORE); - } - } + MapNode getNodeNoEx(v3s16 p); void unspreadLight(enum LightBank bank, core::map & from_nodes, @@ -273,23 +223,33 @@ public: virtual void save(bool only_changed){assert(0);}; - // Server implements this + // Server implements this. + // Client leaves it as no-op. virtual void saveBlock(MapBlock *block){}; /* - Updates usage timers + Updates usage timers and unloads unused blocks and sectors. + Saves modified blocks before unloading on MAPTYPE_SERVER. */ - void timerUpdate(float dtime); - + void timerUpdate(float dtime, float unload_timeout, + core::list *unloaded_blocks=NULL); + + // Deletes sectors and their blocks from memory // Takes cache into account - // sector mutex should be locked when calling - void deleteSectors(core::list &list, bool only_blocks); - - // Returns count of deleted sectors - u32 unloadUnusedData(float timeout, bool only_blocks=false, - core::list *deleted_blocks=NULL); + // If deleted sector is in sector cache, clears cache + void deleteSectors(core::list &list); - // For debug printing +#if 0 + /* + Unload unused data + = flush changed to disk and delete from memory, if usage timer of + block is more than timeout + */ + void unloadUnusedData(float timeout, + core::list *deleted_blocks=NULL); +#endif + + // For debug printing. Prints "Map: ", "ServerMap: " or "ClientMap: " virtual void PrintInfo(std::ostream &out); void transformLiquids(core::map & modified_blocks); @@ -321,7 +281,6 @@ protected: core::map m_event_receivers; core::map m_sectors; - //JMutex m_sector_mutex; // Be sure to set this to NULL when the cached sector is deleted MapSector *m_sector_cache; @@ -379,24 +338,13 @@ public: */ MapBlock * createBlock(v3s16 p); -#if 0 /* - NOTE: This comment might be outdated - Forcefully get a block from somewhere. - - InvalidPositionException possible if only_from_disk==true - - Parameters: - changed_blocks: Blocks that have been modified + - Memory + - Load from disk + - Generate */ - MapBlock * emergeBlock( - v3s16 p, - bool only_from_disk, - core::map &changed_blocks, - core::map &lighting_invalidated_blocks - ); -#endif + MapBlock * emergeBlock(v3s16 p, bool allow_generate=true); // Helper for placing objects on ground level s16 findGroundLevel(v2s16 p2d); @@ -547,7 +495,7 @@ public: */ MapSector * emergeSector(v2s16 p); - void deSerializeSector(v2s16 p2d, std::istream &is); + //void deSerializeSector(v2s16 p2d, std::istream &is); /* ISceneNode methods diff --git a/src/mapblock.cpp b/src/mapblock.cpp index 2f6a4b850..647a17756 100644 --- a/src/mapblock.cpp +++ b/src/mapblock.cpp @@ -28,7 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc., MapBlock */ -MapBlock::MapBlock(NodeContainer *parent, v3s16 pos, bool dummy): +MapBlock::MapBlock(Map *parent, v3s16 pos, bool dummy): m_parent(parent), m_pos(pos), m_modified(MOD_STATE_WRITE_NEEDED), @@ -38,7 +38,7 @@ MapBlock::MapBlock(NodeContainer *parent, v3s16 pos, bool dummy): m_generated(false), m_objects(this), m_timestamp(BLOCK_TIMESTAMP_UNDEFINED), - m_usage_timer(BLOCK_TIMESTAMP_UNDEFINED) + m_usage_timer(0) { data = NULL; if(dummy == false) @@ -607,24 +607,20 @@ void MapBlock::serialize(std::ostream &os, u8 version) Get data */ + // Serialize nodes + SharedBuffer databuf_nodelist(nodecount*3); + for(u32 i=0; i databuf(nodecount*3); - - // Get contents for(u32 i=0; id<getParent(); - // This will only work if the parent is the map - assert(parentcontainer->nodeContainerId() == NODECONTAINER_ID_MAP); - // OK, we have the map! - Map *map = (Map*)parentcontainer; + Map *map = block->getParent(); for(u16 i=0; i<6; i++) { diff --git a/src/mapblockobject.cpp b/src/mapblockobject.cpp index 009163a18..ab1c20267 100644 --- a/src/mapblockobject.cpp +++ b/src/mapblockobject.cpp @@ -25,6 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "map.h" #include "inventory.h" #include "utility.h" +#include "mapblock.h" /* MapBlockObject @@ -856,16 +857,7 @@ bool MapBlockObjectList::wrapObject(MapBlockObject *object) assert(m_objects.find(object->m_id) != NULL); assert(m_objects[object->m_id] == object); - NodeContainer *parentcontainer = m_block->getParent(); - // This will only work if the parent is the map - if(parentcontainer->nodeContainerId() != NODECONTAINER_ID_MAP) - { - dstream<<"WARNING: Wrapping object not possible: " - "MapBlock's parent is not map"<getParent(); // Calculate blockpos on map v3s16 oldblock_pos_i_on_map = m_block->getPosRelative(); diff --git a/src/mapchunk.h b/src/mapchunk.h index 9860abad0..98df7ce66 100644 --- a/src/mapchunk.h +++ b/src/mapchunk.h @@ -20,6 +20,11 @@ with this program; if not, write to the Free Software Foundation, Inc., #ifndef MAPCHUNK_HEADER #define MAPCHUNK_HEADER +/* + TODO: Remove +*/ + +#if 0 /* MapChunk contains map-generation-time metadata for an area of some MapSectors. (something like 16x16) @@ -66,6 +71,7 @@ private: u8 m_generation_level; bool m_modified; }; +#endif #endif diff --git a/src/mapgen.cpp b/src/mapgen.cpp index a491ac81a..d7b6e56c4 100644 --- a/src/mapgen.cpp +++ b/src/mapgen.cpp @@ -23,8 +23,9 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "noise.h" #include "mapblock.h" #include "map.h" -#include "serverobject.h" #include "mineral.h" +//#include "serverobject.h" +#include "content_sao.h" namespace mapgen { @@ -531,7 +532,7 @@ static void make_corridor(VoxelManipulator &vmanip, v3s16 doorplace, else length = random.range(1,6); length = random.range(1,13); - u32 partlength = random.range(1,length); + u32 partlength = random.range(1,13); u32 partcount = 0; s16 make_stairs = 0; if(random.next()%2 == 0 && partlength >= 3) @@ -700,14 +701,30 @@ public: continue; v3s16 roomplace; // X east, Z north, Y up +#if 1 if(doordir == v3s16(1,0,0)) // X+ - roomplace = doorplace + v3s16(0,-1,-roomsize.Z/2+m_random.range(-roomsize.Z/2+1,roomsize.Z/2-1)); + roomplace = doorplace + + v3s16(0,-1,m_random.range(-roomsize.Z+2,-2)); if(doordir == v3s16(-1,0,0)) // X- - roomplace = doorplace + v3s16(-roomsize.X+1,-1,-roomsize.Z/2+m_random.range(-roomsize.Z/2+1,roomsize.Z/2-1)); + roomplace = doorplace + + v3s16(-roomsize.X+1,-1,m_random.range(-roomsize.Z+2,-2)); if(doordir == v3s16(0,0,1)) // Z+ - roomplace = doorplace + v3s16(-roomsize.X/2+m_random.range(-roomsize.X/2+1,roomsize.X/2-1),-1,0); + roomplace = doorplace + + v3s16(m_random.range(-roomsize.X+2,-2),-1,0); if(doordir == v3s16(0,0,-1)) // Z- - roomplace = doorplace + v3s16(-roomsize.X/2+m_random.range(-roomsize.X/2+1,roomsize.X/2-1),-1,-roomsize.Z+1); + roomplace = doorplace + + v3s16(m_random.range(-roomsize.X+2,-2),-1,-roomsize.Z+1); +#endif +#if 0 + if(doordir == v3s16(1,0,0)) // X+ + roomplace = doorplace + v3s16(0,-1,-roomsize.Z/2); + if(doordir == v3s16(-1,0,0)) // X- + roomplace = doorplace + v3s16(-roomsize.X+1,-1,-roomsize.Z/2); + if(doordir == v3s16(0,0,1)) // Z+ + roomplace = doorplace + v3s16(-roomsize.X/2,-1,0); + if(doordir == v3s16(0,0,-1)) // Z- + roomplace = doorplace + v3s16(-roomsize.X/2,-1,-roomsize.Z+1); +#endif // Check fit bool fits = true; @@ -818,7 +835,7 @@ static void make_dungeon1(VoxelManipulator &vmanip, PseudoRandom &random) // Determine walker start position - bool start_in_last_room = (random.range(0,1)==0); + bool start_in_last_room = (random.range(0,2)!=0); //bool start_in_last_room = true; v3s16 walker_start_place; @@ -877,30 +894,47 @@ static void make_dungeon1(VoxelManipulator &vmanip, PseudoRandom &random) Noise functions. Make sure seed is mangled differently in each one. */ -// This affects the shape of the contour +/* + Scaling the output of the noise function affects the overdrive of the + contour function, which affects the shape of the output considerably. +*/ +#define CAVE_NOISE_SCALE 12.0 //#define CAVE_NOISE_SCALE 10.0 //#define CAVE_NOISE_SCALE 7.5 -#define CAVE_NOISE_SCALE 5.0 +//#define CAVE_NOISE_SCALE 5.0 +//#define CAVE_NOISE_SCALE 1.0 + +//#define CAVE_NOISE_THRESHOLD (2.5/CAVE_NOISE_SCALE) +#define CAVE_NOISE_THRESHOLD (1.5/CAVE_NOISE_SCALE) NoiseParams get_cave_noise1_params(u64 seed) { /*return NoiseParams(NOISE_PERLIN_CONTOUR, seed+52534, 5, 0.7, 200, CAVE_NOISE_SCALE);*/ - return NoiseParams(NOISE_PERLIN_CONTOUR, seed+52534, 4, 0.7, - 100, CAVE_NOISE_SCALE); + /*return NoiseParams(NOISE_PERLIN_CONTOUR, seed+52534, 4, 0.7, + 100, CAVE_NOISE_SCALE);*/ + /*return NoiseParams(NOISE_PERLIN_CONTOUR, seed+52534, 5, 0.6, + 100, CAVE_NOISE_SCALE);*/ + /*return NoiseParams(NOISE_PERLIN_CONTOUR, seed+52534, 5, 0.3, + 100, CAVE_NOISE_SCALE);*/ + return NoiseParams(NOISE_PERLIN_CONTOUR, seed+52534, 4, 0.5, + 50, CAVE_NOISE_SCALE); + //return NoiseParams(NOISE_CONSTANT_ONE); } NoiseParams get_cave_noise2_params(u64 seed) { /*return NoiseParams(NOISE_PERLIN_CONTOUR_FLIP_YZ, seed+10325, 5, 0.7, 200, CAVE_NOISE_SCALE);*/ - return NoiseParams(NOISE_PERLIN_CONTOUR_FLIP_YZ, seed+10325, 4, 0.7, - 100, CAVE_NOISE_SCALE); + /*return NoiseParams(NOISE_PERLIN_CONTOUR_FLIP_YZ, seed+10325, 4, 0.7, + 100, CAVE_NOISE_SCALE);*/ + /*return NoiseParams(NOISE_PERLIN_CONTOUR_FLIP_YZ, seed+10325, 5, 0.3, + 100, CAVE_NOISE_SCALE);*/ + return NoiseParams(NOISE_PERLIN_CONTOUR_FLIP_YZ, seed+10325, 4, 0.5, + 50, CAVE_NOISE_SCALE); + //return NoiseParams(NOISE_CONSTANT_ONE); } -//#define CAVE_NOISE_THRESHOLD (2.5/CAVE_NOISE_SCALE) -#define CAVE_NOISE_THRESHOLD (2.0/CAVE_NOISE_SCALE) - NoiseParams get_ground_noise1_params(u64 seed) { return NoiseParams(NOISE_PERLIN, seed+983240, 4, @@ -937,13 +971,13 @@ bool val_is_ground(double ground_noise1_val, v3s16 p, u64 seed) { //return ((double)p.Y < ground_noise1_val); - double f = 0.8 + noise2d_perlin( + double f = 0.55 + noise2d_perlin( 0.5+(float)p.X/250, 0.5+(float)p.Z/250, seed+920381, 3, 0.45); if(f < 0.01) f = 0.01; else if(f >= 1.0) - f *= 2.0; + f *= 1.6; double h = WATER_LEVEL + 10 * noise2d_perlin( 0.5+(float)p.X/250, 0.5+(float)p.Z/250, seed+84174, 4, 0.5); @@ -1082,6 +1116,7 @@ double get_sector_maximum_ground_level(u64 seed, v2s16 sectorpos, double p) v2s16 node_min = sectorpos*MAP_BLOCKSIZE; v2s16 node_max = (sectorpos+v2s16(1,1))*MAP_BLOCKSIZE-v2s16(1,1); double a = -31000; + // Corners a = MYMAX(a, find_ground_level_from_noise(seed, v2s16(node_min.X, node_min.Y), p)); a = MYMAX(a, find_ground_level_from_noise(seed, @@ -1090,8 +1125,18 @@ double get_sector_maximum_ground_level(u64 seed, v2s16 sectorpos, double p) v2s16(node_max.X, node_max.Y), p)); a = MYMAX(a, find_ground_level_from_noise(seed, v2s16(node_min.X, node_min.Y), p)); + // Center a = MYMAX(a, find_ground_level_from_noise(seed, v2s16(node_min.X+MAP_BLOCKSIZE/2, node_min.Y+MAP_BLOCKSIZE/2), p)); + // Side middle points + a = MYMAX(a, find_ground_level_from_noise(seed, + v2s16(node_min.X+MAP_BLOCKSIZE/2, node_min.Y), p)); + a = MYMAX(a, find_ground_level_from_noise(seed, + v2s16(node_min.X+MAP_BLOCKSIZE/2, node_max.Y), p)); + a = MYMAX(a, find_ground_level_from_noise(seed, + v2s16(node_min.X, node_min.Y+MAP_BLOCKSIZE/2), p)); + a = MYMAX(a, find_ground_level_from_noise(seed, + v2s16(node_max.X, node_min.Y+MAP_BLOCKSIZE/2), p)); return a; } @@ -1102,6 +1147,7 @@ double get_sector_minimum_ground_level(u64 seed, v2s16 sectorpos, double p) v2s16 node_min = sectorpos*MAP_BLOCKSIZE; v2s16 node_max = (sectorpos+v2s16(1,1))*MAP_BLOCKSIZE-v2s16(1,1); double a = 31000; + // Corners a = MYMIN(a, find_ground_level_from_noise(seed, v2s16(node_min.X, node_min.Y), p)); a = MYMIN(a, find_ground_level_from_noise(seed, @@ -1110,8 +1156,18 @@ double get_sector_minimum_ground_level(u64 seed, v2s16 sectorpos, double p) v2s16(node_max.X, node_max.Y), p)); a = MYMIN(a, find_ground_level_from_noise(seed, v2s16(node_min.X, node_min.Y), p)); + // Center a = MYMIN(a, find_ground_level_from_noise(seed, v2s16(node_min.X+MAP_BLOCKSIZE/2, node_min.Y+MAP_BLOCKSIZE/2), p)); + // Side middle points + a = MYMIN(a, find_ground_level_from_noise(seed, + v2s16(node_min.X+MAP_BLOCKSIZE/2, node_min.Y), p)); + a = MYMIN(a, find_ground_level_from_noise(seed, + v2s16(node_min.X+MAP_BLOCKSIZE/2, node_max.Y), p)); + a = MYMIN(a, find_ground_level_from_noise(seed, + v2s16(node_min.X, node_min.Y+MAP_BLOCKSIZE/2), p)); + a = MYMIN(a, find_ground_level_from_noise(seed, + v2s16(node_max.X, node_min.Y+MAP_BLOCKSIZE/2), p)); return a; } @@ -1358,12 +1414,12 @@ void make_block(BlockMakeData *data) If block is deep underground, this is set to true and ground density noise is not generated, for speed optimization. */ - bool all_is_ground_except_caves = (minimum_ground_depth > 16); + bool all_is_ground_except_caves = (minimum_ground_depth > 40); /* Create a block-specific seed */ - u32 blockseed = (data->seed%0x100000000) + full_node_min.Z*38134234 + u32 blockseed = (u32)(data->seed%0x100000000) + full_node_min.Z*38134234 + full_node_min.Y*42123 + full_node_min.X*23; /* @@ -1385,13 +1441,13 @@ void make_block(BlockMakeData *data) /* Cave noise */ - +#if 1 noisebuf_cave.create(get_cave_noise1_params(data->seed), minpos_f.X, minpos_f.Y, minpos_f.Z, maxpos_f.X, maxpos_f.Y, maxpos_f.Z, - 4, 3, 4); - + 2, 2, 2); noisebuf_cave.multiply(get_cave_noise2_params(data->seed)); +#endif /* Ground noise diff --git a/src/mapnode.cpp b/src/mapnode.cpp index dae21e7cc..391e593f9 100644 --- a/src/mapnode.cpp +++ b/src/mapnode.cpp @@ -30,8 +30,8 @@ with this program; if not, write to the Free Software Foundation, Inc., ContentFeatures::~ContentFeatures() { - if(translate_to) - delete translate_to; + /*if(translate_to) + delete translate_to;*/ if(initial_metadata) delete initial_metadata; } @@ -138,6 +138,17 @@ void init_mapnode() f->tiles[j].material_type = initial_material_type; } + /* + Initially set every block to be shown as an unknown block. + Don't touch CONTENT_IGNORE or CONTENT_AIR. + */ + for(u16 i=0; i<=253; i++) + { + ContentFeatures *f = &g_content_features[i]; + f->setAllTextures("unknown_block.png"); + f->dug_item = std::string("MaterialItem ")+itos(i)+" 1"; + } + /* Initialize mapnode content */ @@ -230,6 +241,94 @@ u8 MapNode::getMineral() return MINERAL_NONE; } +u32 MapNode::serializedLength(u8 version) +{ + if(!ser_ver_supported(version)) + throw VersionMismatchException("ERROR: MapNode format not supported"); + + if(version == 0) + return 1; + else if(version <= 9) + return 2; + else + return 3; +} +void MapNode::serialize(u8 *dest, u8 version) +{ + if(!ser_ver_supported(version)) + throw VersionMismatchException("ERROR: MapNode format not supported"); + + u8 actual_d = d; + + // Convert from new version to old + if(version <= 18) + { + // In these versions, CONTENT_IGNORE and CONTENT_AIR + // are 255 and 254 + if(d == CONTENT_IGNORE) + d = 255; + else if(d == CONTENT_AIR) + d = 254; + } + + if(version == 0) + { + dest[0] = actual_d; + } + else if(version <= 9) + { + dest[0] = actual_d; + dest[1] = param; + } + else + { + dest[0] = actual_d; + dest[1] = param; + dest[2] = param2; + } +} +void MapNode::deSerialize(u8 *source, u8 version) +{ + if(!ser_ver_supported(version)) + throw VersionMismatchException("ERROR: MapNode format not supported"); + + if(version == 0) + { + d = source[0]; + } + else if(version == 1) + { + d = source[0]; + // This version doesn't support saved lighting + if(light_propagates() || light_source() > 0) + param = 0; + else + param = source[1]; + } + else if(version <= 9) + { + d = source[0]; + param = source[1]; + } + else + { + d = source[0]; + param = source[1]; + param2 = source[2]; + + // Convert from old version to new + if(version <= 18) + { + // In these versions, CONTENT_IGNORE and CONTENT_AIR + // are 255 and 254 + if(d == 255) + d = CONTENT_IGNORE; + else if(d == 254) + d = CONTENT_AIR; + } + } +} + /* Gets lighting value at face of node diff --git a/src/mapnode.h b/src/mapnode.h index d4ba0fed5..8bbd4eb79 100644 --- a/src/mapnode.h +++ b/src/mapnode.h @@ -36,6 +36,13 @@ with this program; if not, write to the Free Software Foundation, Inc., - Tile = TileSpec at some side of a node of some content type */ +/* + Ranges: + 0x000...0x07f: param2 is fully usable + 0x800...0xfff: param2 lower 4 bytes are free +*/ +typedef u16 content_t; + /* Initializes all kind of stuff in here. Many things depend on this. @@ -59,14 +66,16 @@ void init_mapnode(); Doesn't create faces with anything and is considered being out-of-map in the game map. */ -#define CONTENT_IGNORE 255 +//#define CONTENT_IGNORE 255 +#define CONTENT_IGNORE 127 #define CONTENT_IGNORE_DEFAULT_PARAM 0 /* The common material through which the player can walk and which is transparent to light */ -#define CONTENT_AIR 254 +//#define CONTENT_AIR 254 +#define CONTENT_AIR 126 /* Content feature list @@ -94,7 +103,7 @@ class NodeMetadata; struct ContentFeatures { // If non-NULL, content is translated to this when deserialized - MapNode *translate_to; + //MapNode *translate_to; // Type of MapNode::param ContentParamType param_type; @@ -156,7 +165,7 @@ struct ContentFeatures void reset() { - translate_to = NULL; + //translate_to = NULL; param_type = CPT_NONE; inventory_texture = NULL; is_ground_content = false; @@ -196,6 +205,8 @@ struct ContentFeatures { setTexture(i, name, alpha); } + // Force inventory texture too + setInventoryTexture(name); } void setTile(u16 i, const TileSpec &tile) @@ -399,20 +410,30 @@ enum LightBank struct MapNode { - // Content - u8 d; + /* + Main content + 0x00-0x7f: Short content type + 0x80-0xff: Long content type (param2>>4 makes up low bytes) + */ + union + { + u8 param0; + u8 d; + }; /* Misc parameter. Initialized to 0. - For light_propagates() blocks, this is light intensity, stored logarithmically from 0 to LIGHT_MAX. Sunlight is LIGHT_SUN, which is LIGHT_MAX+1. - - Contains 2 values, day- and night lighting. Each takes 4 bits. + - Contains 2 values, day- and night lighting. Each takes 4 bits. + - Mineral content (should be removed from here) + - Uhh... well, most blocks have light or nothing in here. */ union { - s8 param; u8 param1; + s8 param; }; /* @@ -437,14 +458,6 @@ struct MapNode param2 = a_param2; } - /*MapNode & operator=(const MapNode &other) - { - d = other.d; - param = other.param; - param2 = other.param2; - return *this; - }*/ - bool operator==(const MapNode &other) { return (d == other.d @@ -452,6 +465,16 @@ struct MapNode && param2 == other.param2); } + // To be used everywhere + content_t getContent() + { + return d; + } + void setContent(content_t c) + { + d = c; + } + /* These four are DEPRECATED I guess. -c55 */ @@ -566,88 +589,15 @@ struct MapNode MINERAL_NONE if doesn't contain or isn't able to contain mineral. */ u8 getMineral(); - + /* - These serialization functions are used when informing client - of a single node add. - - NOTE: When loading a MapBlock, these are not used. Should they? + Serialization functions */ - static u32 serializedLength(u8 version) - { - if(!ser_ver_supported(version)) - throw VersionMismatchException("ERROR: MapNode format not supported"); - - if(version == 0) - return 1; - else if(version <= 9) - return 2; - else - return 3; - } - void serialize(u8 *dest, u8 version) - { - if(!ser_ver_supported(version)) - throw VersionMismatchException("ERROR: MapNode format not supported"); - - if(version == 0) - { - dest[0] = d; - } - else if(version <= 9) - { - dest[0] = d; - dest[1] = param; - } - else - { - dest[0] = d; - dest[1] = param; - dest[2] = param2; - } - } - void deSerialize(u8 *source, u8 version) - { - if(!ser_ver_supported(version)) - throw VersionMismatchException("ERROR: MapNode format not supported"); - - if(version == 0) - { - d = source[0]; - } - else if(version == 1) - { - d = source[0]; - // This version doesn't support saved lighting - if(light_propagates() || light_source() > 0) - param = 0; - else - param = source[1]; - } - else if(version <= 9) - { - d = source[0]; - param = source[1]; - } - else - { - d = source[0]; - param = source[1]; - param2 = source[2]; - } - - // Translate deprecated stuff - // NOTE: This doesn't get used because MapBlock handles node - // parameters directly - MapNode *translate_to = content_features(d).translate_to; - if(translate_to) - { - dstream<<"MapNode: WARNING: Translating "<d<getPos().Y; - { - JMutexAutoLock lock(m_mutex); - - MapBlock *block2 = getBlockBuffered(block_y); - if(block2 != NULL){ - throw AlreadyExistsException("Block already exists"); - } - - v2s16 p2d(block->getPos().X, block->getPos().Z); - assert(p2d == m_pos); - - // Insert into container - m_blocks.insert(block_y, block); + MapBlock *block2 = getBlockBuffered(block_y); + if(block2 != NULL){ + throw AlreadyExistsException("Block already exists"); } + + v2s16 p2d(block->getPos().X, block->getPos().Z); + assert(p2d == m_pos); + + // Insert into container + m_blocks.insert(block_y, block); } -void MapSector::removeBlock(MapBlock *block) +void MapSector::deleteBlock(MapBlock *block) { s16 block_y = block->getPos().Y; - JMutexAutoLock lock(m_mutex); - // Clear from cache m_block_cache = NULL; // Remove from container m_blocks.remove(block_y); + + // Delete + delete block; } void MapSector::getBlocks(core::list &dest) { - JMutexAutoLock lock(m_mutex); - core::list ref_list; core::map::Iterator bi; @@ -175,7 +151,7 @@ void MapSector::getBlocks(core::list &dest) ServerMapSector */ -ServerMapSector::ServerMapSector(NodeContainer *parent, v2s16 pos): +ServerMapSector::ServerMapSector(Map *parent, v2s16 pos): MapSector(parent, pos) { } @@ -184,15 +160,6 @@ ServerMapSector::~ServerMapSector() { } -f32 ServerMapSector::getGroundHeight(v2s16 p, bool generate) -{ - return GROUNDHEIGHT_NOTFOUND_SETVALUE; -} - -void ServerMapSector::setGroundHeight(v2s16 p, f32 y, bool generate) -{ -} - void ServerMapSector::serialize(std::ostream &os, u8 version) { if(!ser_ver_supported(version)) @@ -217,7 +184,7 @@ void ServerMapSector::serialize(std::ostream &os, u8 version) ServerMapSector* ServerMapSector::deSerialize( std::istream &is, - NodeContainer *parent, + Map *parent, v2s16 p2d, core::map & sectors ) @@ -280,7 +247,7 @@ ServerMapSector* ServerMapSector::deSerialize( ClientMapSector */ -ClientMapSector::ClientMapSector(NodeContainer *parent, v2s16 pos): +ClientMapSector::ClientMapSector(Map *parent, v2s16 pos): MapSector(parent, pos) { } @@ -289,45 +256,6 @@ ClientMapSector::~ClientMapSector() { } -void ClientMapSector::deSerialize(std::istream &is) -{ - /* - [0] u8 serialization version - [1] s16 corners[0] - [3] s16 corners[1] - [5] s16 corners[2] - [7] s16 corners[3] - size = 9 - - In which corners are in these positions - v2s16(0,0), - v2s16(1,0), - v2s16(1,1), - v2s16(0,1), - */ - - // Read version - u8 version = SER_FMT_VER_INVALID; - is.read((char*)&version, 1); - - if(!ser_ver_supported(version)) - throw VersionMismatchException("ERROR: MapSector format not supported"); - - u8 buf[2]; - - // Dummy read corners - is.read((char*)buf, 2); - is.read((char*)buf, 2); - is.read((char*)buf, 2); - is.read((char*)buf, 2); - - /* - Set stuff in sector - */ - - // Nothing here - -} #endif // !SERVER //END diff --git a/src/mapsector.h b/src/mapsector.h index fda290cd7..44f45d8f0 100644 --- a/src/mapsector.h +++ b/src/mapsector.h @@ -26,9 +26,11 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include "common_irrlicht.h" -#include "mapblock.h" -//#include "heightmap.h" #include "exceptions.h" +#include + +class MapBlock; +class Map; /* This is an Y-wise stack of MapBlocks. @@ -37,18 +39,13 @@ with this program; if not, write to the Free Software Foundation, Inc., #define MAPSECTOR_SERVER 0 #define MAPSECTOR_CLIENT 1 -class MapSector: public NodeContainer +class MapSector { public: - MapSector(NodeContainer *parent, v2s16 pos); + MapSector(Map *parent, v2s16 pos); virtual ~MapSector(); - virtual u16 nodeContainerId() const - { - return NODECONTAINER_ID_MAPSECTOR; - } - virtual u32 getId() const = 0; void deleteBlocks(); @@ -59,167 +56,32 @@ public: } MapBlock * getBlockNoCreateNoEx(s16 y); - MapBlock * getBlockNoCreate(s16 y); MapBlock * createBlankBlockNoInsert(s16 y); MapBlock * createBlankBlock(s16 y); - //MapBlock * getBlock(s16 y, bool generate=true); void insertBlock(MapBlock *block); - // This is used to remove a dummy from the sector while generating it. - // Block is only removed from internal container, not deleted. - void removeBlock(MapBlock *block); + void deleteBlock(MapBlock *block); - /* - This might not be a thread-safe depending on the day. - See the implementation. - */ void getBlocks(core::list &dest); - /* - If all nodes in area can be accessed, returns true and - adds all blocks in area to blocks. - - If all nodes in area cannot be accessed, returns false. - - The implementation of this is quite slow - - if blocks==NULL; it is not accessed at all. - */ - bool isValidArea(v3s16 p_min_nodes, v3s16 p_max_nodes, - core::map *blocks) - { - core::map bs; - - v3s16 p_min = getNodeBlockPos(p_min_nodes); - v3s16 p_max = getNodeBlockPos(p_max_nodes); - if(p_min.X != 0 || p_min.Z != 0 - || p_max.X != 0 || p_max.Z != 0) - return false; - v3s16 y; - for(s16 y=p_min.Y; y<=p_max.Y; y++) - { - try{ - MapBlock *block = getBlockNoCreate(y); - if(block->isDummy()) - return false; - if(blocks!=NULL) - bs[y] = block; - } - catch(InvalidPositionException &e) - { - return false; - } - } - - if(blocks!=NULL) - { - for(core::map::Iterator i=bs.getIterator(); - i.atEnd()==false; i++) - { - MapBlock *block = i.getNode()->getValue(); - s16 y = i.getNode()->getKey(); - blocks->insert(y, block); - } - } - return true; - } - - void getBlocksInArea(v3s16 p_min_nodes, v3s16 p_max_nodes, - core::map &blocks) - { - v3s16 p_min = getNodeBlockPos(p_min_nodes); - v3s16 p_max = getNodeBlockPos(p_max_nodes); - v3s16 y; - for(s16 y=p_min.Y; y<=p_max.Y; y++) - { - try{ - MapBlock *block = getBlockNoCreate(y); - blocks.insert(block->getPos(), block); - } - catch(InvalidPositionException &e) - { - } - } - } - - // virtual from NodeContainer - bool isValidPosition(v3s16 p) - { - v3s16 blockpos = getNodeBlockPos(p); - - if(blockpos.X != 0 || blockpos.Z != 0) - return false; - - MapBlock *blockref; - try{ - blockref = getBlockNoCreate(blockpos.Y); - } - catch(InvalidPositionException &e) - { - return false; - } - - return true; - } - - // virtual from NodeContainer - MapNode getNode(v3s16 p) - { - v3s16 blockpos = getNodeBlockPos(p); - if(blockpos.X != 0 || blockpos.Z != 0) - throw InvalidPositionException - ("MapSector only allows Y"); - - MapBlock * blockref = getBlockNoCreate(blockpos.Y); - v3s16 relpos = p - blockpos*MAP_BLOCKSIZE; - - return blockref->getNode(relpos); - } - // virtual from NodeContainer - void setNode(v3s16 p, MapNode & n) - { - v3s16 blockpos = getNodeBlockPos(p); - if(blockpos.X != 0 || blockpos.Z != 0) - throw InvalidPositionException - ("MapSector only allows Y"); - - MapBlock * blockref = getBlockNoCreate(blockpos.Y); - v3s16 relpos = p - blockpos*MAP_BLOCKSIZE; - blockref->setNode(relpos, n); - } - - // DEPRECATED? - virtual f32 getGroundHeight(v2s16 p, bool generate=false) - { - return GROUNDHEIGHT_NOTFOUND_SETVALUE; - } - virtual void setGroundHeight(v2s16 p, f32 y, bool generate=false) - { - } - - // When true, sector metadata is changed from the one on disk - // (sector metadata = all but blocks) - // Basically, this should be changed to true in every setter method + // Always false at the moment, because sector contains no metadata. bool differs_from_disk; protected: // The pile of MapBlocks core::map m_blocks; - //JMutex m_blocks_mutex; // For public access functions - NodeContainer *m_parent; + Map *m_parent; // Position on parent (in MapBlock widths) v2s16 m_pos; - + + // Last-used block is cached here for quicker access. // Be sure to set this to NULL when the cached block is deleted MapBlock *m_block_cache; s16 m_block_cache_y; - // This is used for protecting m_blocks - JMutex m_mutex; - /* Private methods */ @@ -230,27 +92,24 @@ protected: class ServerMapSector : public MapSector { public: - ServerMapSector(NodeContainer *parent, v2s16 pos); + ServerMapSector(Map *parent, v2s16 pos); ~ServerMapSector(); u32 getId() const { return MAPSECTOR_SERVER; } - - // DEPRECATED? - f32 getGroundHeight(v2s16 p, bool generate=false); - void setGroundHeight(v2s16 p, f32 y, bool generate=false); /* These functions handle metadata. They do not handle blocks. */ + void serialize(std::ostream &os, u8 version); static ServerMapSector* deSerialize( std::istream &is, - NodeContainer *parent, + Map *parent, v2s16 p2d, core::map & sectors ); @@ -262,7 +121,7 @@ private: class ClientMapSector : public MapSector { public: - ClientMapSector(NodeContainer *parent, v2s16 pos); + ClientMapSector(Map *parent, v2s16 pos); ~ClientMapSector(); u32 getId() const @@ -270,16 +129,7 @@ public: return MAPSECTOR_CLIENT; } - void deSerialize(std::istream &is); - - /*s16 getCorner(u16 i) - { - return m_corners[i]; - }*/ - private: - // The ground height of the corners is stored in here - //s16 m_corners[4]; }; #endif diff --git a/src/mineral.h b/src/mineral.h index 970ff1f78..61776e669 100644 --- a/src/mineral.h +++ b/src/mineral.h @@ -21,7 +21,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #define MINERAL_HEADER #include "inventory.h" -#include "tile.h" /* Minerals diff --git a/src/noise.cpp b/src/noise.cpp index b755a824a..9c2141ce0 100644 --- a/src/noise.cpp +++ b/src/noise.cpp @@ -238,7 +238,11 @@ double noise3d_param(const NoiseParams ¶m, double x, double y, double z) y /= s; z /= s; - if(param.type == NOISE_PERLIN) + if(param.type == NOISE_CONSTANT_ONE) + { + return 1.0; + } + else if(param.type == NOISE_PERLIN) { return param.noise_scale*noise3d_perlin(x,y,z, param.seed, param.octaves, diff --git a/src/noise.h b/src/noise.h index c8d8985c6..ed75f316d 100644 --- a/src/noise.h +++ b/src/noise.h @@ -82,10 +82,11 @@ double noise3d_perlin_abs(double x, double y, double z, int seed, enum NoiseType { + NOISE_CONSTANT_ONE, NOISE_PERLIN, NOISE_PERLIN_ABS, NOISE_PERLIN_CONTOUR, - NOISE_PERLIN_CONTOUR_FLIP_YZ + NOISE_PERLIN_CONTOUR_FLIP_YZ, }; struct NoiseParams diff --git a/src/player.cpp b/src/player.cpp index 198eca957..6bacb088d 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -309,6 +309,8 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d, v3f oldpos = position; v3s16 oldpos_i = floatToInt(oldpos, BS); + v3f old_speed = m_speed; + /*std::cout<<"oldpos_i=("<push_back(info); + } + }*/ } } @@ -557,13 +574,13 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d, */ if(other_axes_overlap && main_axis_collides) { - v3f old_speed = m_speed; + //v3f old_speed = m_speed; m_speed -= m_speed.dotProduct(dirs[i]) * dirs[i]; position -= position.dotProduct(dirs[i]) * dirs[i]; position += oldpos.dotProduct(dirs[i]) * dirs[i]; - if(collision_info) + /*if(collision_info) { // Report fall collision if(old_speed.Y < m_speed.Y - 0.1) @@ -573,7 +590,7 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d, info.speed = m_speed.Y - old_speed.Y; collision_info->push_back(info); } - } + }*/ } } @@ -656,6 +673,21 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d, Set new position */ setPosition(position); + + /* + Report collisions + */ + if(collision_info) + { + // Report fall collision + if(old_speed.Y < m_speed.Y - 0.1) + { + CollisionInfo info; + info.t = COLLISION_FALL; + info.speed = m_speed.Y - old_speed.Y; + collision_info->push_back(info); + } + } } void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d) diff --git a/src/serialization.h b/src/serialization.h index 80a336101..974ae95d8 100644 --- a/src/serialization.h +++ b/src/serialization.h @@ -53,12 +53,13 @@ with this program; if not, write to the Free Software Foundation, Inc., 15: StaticObjects 16: larger maximum size of node metadata, and compression 17: MapBlocks contain timestamp - 18: sqlite/new generator/whatever + 18: new generator (not really necessary, but it's there) + 19: new content type handling */ // This represents an uninitialized or invalid format #define SER_FMT_VER_INVALID 255 // Highest supported serialization version -#define SER_FMT_VER_HIGHEST 18 +#define SER_FMT_VER_HIGHEST 19 // Lowest supported serialization version #define SER_FMT_VER_LOWEST 0 diff --git a/src/server.cpp b/src/server.cpp index cf8b57773..c2433e1af 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -34,6 +34,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "content_mapnode.h" #include "content_craft.h" #include "content_nodemeta.h" +#include "mapblock.h" +#include "serverobject.h" #define BLOCK_EMERGE_FLAG_FROMDISK (1<<0) @@ -600,6 +602,9 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime, bool block_is_invalid = false; if(block != NULL) { + // Reset usage timer, this block will be of use in the future. + block->resetUsageTimer(); + // Block is dummy if data doesn't exist. // It means it has been not found from disk and not generated if(block->isDummy()) @@ -1295,12 +1300,21 @@ void Server::AsyncRunStep() } { - // Step environment - // This also runs Map's timers JMutexAutoLock lock(m_env_mutex); + // Step environment ScopeProfiler sp(&g_profiler, "Server: environment step"); m_env.step(dtime); } + + const float map_timer_and_unload_dtime = 5.15; + if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime)) + { + JMutexAutoLock lock(m_env_mutex); + // Run Map's timers and unload unused data + ScopeProfiler sp(&g_profiler, "Server: map timer and unload"); + m_env.getMap().timerUpdate(map_timer_and_unload_dtime, + g_settings.getFloat("server_unload_unused_data_timeout")); + } /* Do background stuff @@ -1656,9 +1670,22 @@ void Server::AsyncRunStep() */ { // Don't send too many at a time - u32 count = 0; + //u32 count = 0; + + // Single change sending is disabled if queue size is not small + bool disable_single_change_sending = false; + if(m_unsent_map_edit_queue.size() >= 4) + disable_single_change_sending = true; + + bool got_any_events = false; + + // We'll log the amount of each + Profiler prof; + while(m_unsent_map_edit_queue.size() != 0) { + got_any_events = true; + MapEditEvent* event = m_unsent_map_edit_queue.pop_front(); // Players far away from the change are stored here. @@ -1668,28 +1695,41 @@ void Server::AsyncRunStep() if(event->type == MEET_ADDNODE) { - dstream<<"Server: MEET_ADDNODE"<p, event->n, event->already_known_by_peer, - &far_players, 30); + //dstream<<"Server: MEET_ADDNODE"<p, event->n, event->already_known_by_peer, + &far_players, 5); + else + sendAddNode(event->p, event->n, event->already_known_by_peer, + &far_players, 30); } else if(event->type == MEET_REMOVENODE) { - dstream<<"Server: MEET_REMOVENODE"<p, event->already_known_by_peer, - &far_players, 30); + //dstream<<"Server: MEET_REMOVENODE"<p, event->already_known_by_peer, + &far_players, 5); + else + sendRemoveNode(event->p, event->already_known_by_peer, + &far_players, 30); } else if(event->type == MEET_BLOCK_NODE_METADATA_CHANGED) { dstream<<"Server: MEET_BLOCK_NODE_METADATA_CHANGED"<p); } else if(event->type == MEET_OTHER) { + prof.add("MEET_OTHER", 1); dstream<<"WARNING: Server: MEET_OTHER not implemented" <type)< modified_blocks2; - for(core::map::Iterator - i = event->modified_blocks.getIterator(); - i.atEnd()==false; i++) + if(far_players.size() > 0) { - v3s16 p = i.getNode()->getKey(); - modified_blocks2.insert(p, m_env.getMap().getBlockNoCreateNoEx(p)); - } - for(core::list::Iterator - i = far_players.begin(); - i != far_players.end(); i++) - { - u16 peer_id = *i; - RemoteClient *client = getClient(peer_id); - if(client==NULL) - continue; - client->SetBlocksNotSent(modified_blocks2); + // Convert list format to that wanted by SetBlocksNotSent + core::map modified_blocks2; + for(core::map::Iterator + i = event->modified_blocks.getIterator(); + i.atEnd()==false; i++) + { + v3s16 p = i.getNode()->getKey(); + modified_blocks2.insert(p, + m_env.getMap().getBlockNoCreateNoEx(p)); + } + // Set blocks not sent + for(core::list::Iterator + i = far_players.begin(); + i != far_players.end(); i++) + { + u16 peer_id = *i; + RemoteClient *client = getClient(peer_id); + if(client==NULL) + continue; + client->SetBlocksNotSent(modified_blocks2); + } } delete event; - // Don't send too many at a time + /*// Don't send too many at a time count++; if(count >= 1 && m_unsent_map_edit_queue.size() < 100) - break; + break;*/ } + + if(got_any_events) + { + dstream<<"Server: MapEditEvents:"< changed_blocks; - m_env.getMap().nodeMetadataStep(dtime, changed_blocks); - - // Use setBlockNotSent - - for(core::map::Iterator - i = changed_blocks.getIterator(); - i.atEnd() == false; i++) - { - MapBlock *block = i.getNode()->getValue(); - - for(core::map::Iterator - i = m_clients.getIterator(); - i.atEnd()==false; i++) - { - RemoteClient *client = i.getNode()->getValue(); - client->SetBlockNotSent(block->getPos()); - } - } - }*/ - /* Trigger emergethread (it somehow gets to a non-triggered but bysy state sometimes) @@ -1809,26 +1829,29 @@ void Server::AsyncRunStep() // Map JMutexAutoLock lock(m_env_mutex); - if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == true) + + /*// Unload unused data (delete from memory) + m_env.getMap().unloadUnusedData( + g_settings.getFloat("server_unload_unused_sectors_timeout")); + */ + /*u32 deleted_count = m_env.getMap().unloadUnusedData( + g_settings.getFloat("server_unload_unused_sectors_timeout")); + */ + + // Save only changed parts + m_env.getMap().save(true); + + /*if(deleted_count > 0) { - // Save only changed parts - m_env.getMap().save(true); + dout_server<<"Server: Unloaded "< 0) - { - dout_server<<"Server: Unloaded "<getToolName(); } } + + v3f playerpos = player->getPosition(); + v3f objpos = obj->getBasePosition(); + v3f dir = (objpos - playerpos).normalize(); - u16 wear = obj->punch(toolname); + u16 wear = obj->punch(toolname, dir); if(titem) { @@ -2710,9 +2737,31 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) MaterialItem *mitem = (MaterialItem*)item; MapNode n; n.d = mitem->getMaterial(); + + // Calculate direction for wall mounted stuff if(content_features(n.d).wall_mounted) n.dir = packDir(p_under - p_over); - + + // Calculate the direction for furnaces and chests and stuff + if(content_features(n.d).param_type == CPT_FACEDIR_SIMPLE) + { + v3f playerpos = player->getPosition(); + v3f blockpos = intToFloat(p_over, BS) - playerpos; + blockpos = blockpos.normalize(); + n.param1 = 0; + if (fabs(blockpos.X) > fabs(blockpos.Z)) { + if (blockpos.X < 0) + n.param1 = 3; + else + n.param1 = 1; + } else { + if (blockpos.Z < 0) + n.param1 = 2; + else + n.param1 = 0; + } + } + /* Send to all close-by players */ @@ -3286,7 +3335,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) void Server::onMapEditEvent(MapEditEvent *event) { - dstream<<"Server::onMapEditEvent()"<clone(); diff --git a/src/server.h b/src/server.h index b88369ddf..1da004da5 100644 --- a/src/server.h +++ b/src/server.h @@ -534,6 +534,7 @@ private: float m_objectdata_timer; float m_emergethread_trigger_timer; float m_savemap_timer; + IntervalLimiter m_map_timer_and_unload_interval; // NOTE: If connection and environment are both to be locked, // environment shall be locked first. diff --git a/src/serverobject.cpp b/src/serverobject.cpp index d31e9a31c..8acb35f6d 100644 --- a/src/serverobject.cpp +++ b/src/serverobject.cpp @@ -19,9 +19,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "serverobject.h" #include -#include "environment.h" #include "inventory.h" -#include "collision.h" core::map ServerActiveObject::m_types; @@ -71,600 +69,4 @@ void ServerActiveObject::registerType(u16 type, Factory f) } -/* - TestSAO -*/ - -// Prototype -TestSAO proto_TestSAO(NULL, 0, v3f(0,0,0)); - -TestSAO::TestSAO(ServerEnvironment *env, u16 id, v3f pos): - ServerActiveObject(env, id, pos), - m_timer1(0), - m_age(0) -{ - ServerActiveObject::registerType(getType(), create); -} - -ServerActiveObject* TestSAO::create(ServerEnvironment *env, u16 id, v3f pos, - const std::string &data) -{ - return new TestSAO(env, id, pos); -} - -void TestSAO::step(float dtime, Queue &messages, - bool send_recommended) -{ - m_age += dtime; - if(m_age > 10) - { - m_removed = true; - return; - } - - m_base_position.Y += dtime * BS * 2; - if(m_base_position.Y > 8*BS) - m_base_position.Y = 2*BS; - - if(send_recommended == false) - return; - - m_timer1 -= dtime; - if(m_timer1 < 0.0) - { - m_timer1 += 0.125; - //dstream<<"TestSAO: id="< &messages, - bool send_recommended) -{ - assert(m_env); - - const float interval = 0.2; - if(m_move_interval.step(dtime, interval)==false) - return; - dtime = interval; - - core::aabbox3d box(-BS/3.,0.0,-BS/3., BS/3.,BS*2./3.,BS/3.); - collisionMoveResult moveresult; - // Apply gravity - m_speed_f += v3f(0, -dtime*9.81*BS, 0); - // Maximum movement without glitches - f32 pos_max_d = BS*0.25; - // Limit speed - if(m_speed_f.getLength()*dtime > pos_max_d) - m_speed_f *= pos_max_d / (m_speed_f.getLength()*dtime); - v3f pos_f = getBasePosition(); - v3f pos_f_old = pos_f; - moveresult = collisionMoveSimple(&m_env->getMap(), pos_max_d, - box, dtime, pos_f, m_speed_f); - - if(send_recommended == false) - return; - - if(pos_f.getDistanceFrom(m_last_sent_position) > 0.05*BS) - { - setBasePosition(pos_f); - m_last_sent_position = pos_f; - - std::ostringstream os(std::ios::binary); - char buf[6]; - // command (0 = update position) - buf[0] = 0; - os.write(buf, 1); - // pos - writeS32((u8*)buf, m_base_position.X*1000); - os.write(buf, 4); - writeS32((u8*)buf, m_base_position.Y*1000); - os.write(buf, 4); - writeS32((u8*)buf, m_base_position.Z*1000); - os.write(buf, 4); - // create message and add to list - ActiveObjectMessage aom(getId(), false, os.str()); - messages.push_back(aom); - } -} - -std::string ItemSAO::getClientInitializationData() -{ - std::ostringstream os(std::ios::binary); - char buf[6]; - // version - buf[0] = 0; - os.write(buf, 1); - // pos - writeS32((u8*)buf, m_base_position.X*1000); - os.write(buf, 4); - writeS32((u8*)buf, m_base_position.Y*1000); - os.write(buf, 4); - writeS32((u8*)buf, m_base_position.Z*1000); - os.write(buf, 4); - // inventorystring - os< item="< &messages, - bool send_recommended) -{ - assert(m_env); - - if(m_is_active == false) - { - if(m_inactive_interval.step(dtime, 0.5)==false) - return; - } - - /* - The AI - */ - - /*m_age += dtime; - if(m_age > 60) - { - // Die - m_removed = true; - return; - }*/ - - // Apply gravity - m_speed_f.Y -= dtime*9.81*BS; - - /* - Move around if some player is close - */ - bool player_is_close = false; - // Check connected players - core::list players = m_env->getPlayers(true); - core::list::Iterator i; - for(i = players.begin(); - i != players.end(); i++) - { - Player *player = *i; - v3f playerpos = player->getPosition(); - if(m_base_position.getDistanceFrom(playerpos) < BS*10.0) - { - player_is_close = true; - break; - } - } - - m_is_active = player_is_close; - - if(player_is_close == false) - { - m_speed_f.X = 0; - m_speed_f.Z = 0; - } - else - { - // Move around - v3f dir(cos(m_yaw/180*PI),0,sin(m_yaw/180*PI)); - f32 speed = 2*BS; - m_speed_f.X = speed * dir.X; - m_speed_f.Z = speed * dir.Z; - - if(m_touching_ground && (m_oldpos - m_base_position).getLength() - < dtime*speed/2) - { - m_counter1 -= dtime; - if(m_counter1 < 0.0) - { - m_counter1 += 1.0; - m_speed_f.Y = 5.0*BS; - } - } - - { - m_counter2 -= dtime; - if(m_counter2 < 0.0) - { - m_counter2 += (float)(myrand()%100)/100*3.0; - m_yaw += ((float)(myrand()%200)-100)/100*180; - m_yaw = wrapDegrees(m_yaw); - } - } - } - - m_oldpos = m_base_position; - - /* - Move it, with collision detection - */ - - core::aabbox3d box(-BS/3.,0.0,-BS/3., BS/3.,BS*2./3.,BS/3.); - collisionMoveResult moveresult; - // Maximum movement without glitches - f32 pos_max_d = BS*0.25; - // Limit speed - if(m_speed_f.getLength()*dtime > pos_max_d) - m_speed_f *= pos_max_d / (m_speed_f.getLength()*dtime); - v3f pos_f = getBasePosition(); - v3f pos_f_old = pos_f; - moveresult = collisionMoveSimple(&m_env->getMap(), pos_max_d, - box, dtime, pos_f, m_speed_f); - m_touching_ground = moveresult.touching_ground; - - setBasePosition(pos_f); - - if(send_recommended == false) - return; - - if(pos_f.getDistanceFrom(m_last_sent_position) > 0.05*BS) - { - m_last_sent_position = pos_f; - - std::ostringstream os(std::ios::binary); - // command (0 = update position) - writeU8(os, 0); - // pos - writeV3F1000(os, m_base_position); - // yaw - writeF1000(os, m_yaw); - // create message and add to list - ActiveObjectMessage aom(getId(), false, os.str()); - messages.push_back(aom); - } -} - -std::string RatSAO::getClientInitializationData() -{ - std::ostringstream os(std::ios::binary); - // version - writeU8(os, 0); - // pos - writeV3F1000(os, m_base_position); - return os.str(); -} - -std::string RatSAO::getStaticData() -{ - //dstream<<__FUNCTION_NAME<m_hp = hp; - return o; -} - -void Oerkki1SAO::step(float dtime, Queue &messages, - bool send_recommended) -{ - assert(m_env); - - if(m_is_active == false) - { - if(m_inactive_interval.step(dtime, 0.5)==false) - return; - } - - /* - The AI - */ - - m_age += dtime; - if(m_age > 120) - { - // Die - m_removed = true; - return; - } - - // Apply gravity - m_speed_f.Y -= dtime*9.81*BS; - - /* - Move around if some player is close - */ - bool player_is_close = false; - v3f near_player_pos; - // Check connected players - core::list players = m_env->getPlayers(true); - core::list::Iterator i; - for(i = players.begin(); - i != players.end(); i++) - { - Player *player = *i; - v3f playerpos = player->getPosition(); - if(m_base_position.getDistanceFrom(playerpos) < BS*15.0) - { - player_is_close = true; - near_player_pos = playerpos; - break; - } - } - - m_is_active = player_is_close; - - if(player_is_close == false) - { - m_speed_f.X = 0; - m_speed_f.Z = 0; - } - else - { - // Move around - - v3f ndir = near_player_pos - m_base_position; - ndir.Y = 0; - ndir /= ndir.getLength(); - f32 nyaw = 180./PI*atan2(ndir.Z,ndir.X); - if(nyaw < m_yaw - 180) - nyaw += 360; - else if(nyaw > m_yaw + 180) - nyaw -= 360; - m_yaw = 0.95*m_yaw + 0.05*nyaw; - m_yaw = wrapDegrees(m_yaw); - - v3f dir(cos(m_yaw/180*PI),0,sin(m_yaw/180*PI)); - f32 speed = 2*BS; - m_speed_f.X = speed * dir.X; - m_speed_f.Z = speed * dir.Z; - - if(m_touching_ground && (m_oldpos - m_base_position).getLength() - < dtime*speed/2) - { - m_counter1 -= dtime; - if(m_counter1 < 0.0) - { - m_counter1 += 1.0; - // Jump - m_speed_f.Y = 5.0*BS; - } - } - - { - m_counter2 -= dtime; - if(m_counter2 < 0.0) - { - m_counter2 += (float)(myrand()%100)/100*3.0; - //m_yaw += ((float)(myrand()%200)-100)/100*180; - m_yaw += ((float)(myrand()%200)-100)/100*90; - m_yaw = wrapDegrees(m_yaw); - } - } - } - - m_oldpos = m_base_position; - - /* - Move it, with collision detection - */ - - core::aabbox3d box(-BS/3.,0.0,-BS/3., BS/3.,BS*5./3.,BS/3.); - collisionMoveResult moveresult; - // Maximum movement without glitches - f32 pos_max_d = BS*0.25; - // Limit speed - if(m_speed_f.getLength()*dtime > pos_max_d) - m_speed_f *= pos_max_d / (m_speed_f.getLength()*dtime); - v3f pos_f = getBasePosition(); - v3f pos_f_old = pos_f; - moveresult = collisionMoveSimple(&m_env->getMap(), pos_max_d, - box, dtime, pos_f, m_speed_f); - m_touching_ground = moveresult.touching_ground; - - setBasePosition(pos_f); - - if(send_recommended == false) - return; - - if(pos_f.getDistanceFrom(m_last_sent_position) > 0.05*BS) - { - m_last_sent_position = pos_f; - - std::ostringstream os(std::ios::binary); - // command (0 = update position) - writeU8(os, 0); - // pos - writeV3F1000(os, m_base_position); - // yaw - writeF1000(os, m_yaw); - // create message and add to list - ActiveObjectMessage aom(getId(), false, os.str()); - messages.push_back(aom); - } -} - -std::string Oerkki1SAO::getClientInitializationData() -{ - std::ostringstream os(std::ios::binary); - // version - writeU8(os, 0); - // pos - writeV3F1000(os, m_base_position); - return os.str(); -} - -std::string Oerkki1SAO::getStaticData() -{ - //dstream<<__FUNCTION_NAME< &messages, - bool send_recommended){} + virtual void step(float dtime, bool send_recommended){} /* The return value of this is passed to the client-side object @@ -104,7 +103,8 @@ public: If the object doesn't return an item, this will be called. Return value is tool wear. */ - virtual u16 punch(const std::string &toolname){return 0;} + virtual u16 punch(const std::string &toolname, v3f dir) + {return 0;} /* Number of players which know about this object. Object won't be @@ -144,6 +144,11 @@ public: */ v3s16 m_static_block; + /* + Queue of messages to be sent to the client + */ + Queue m_messages_out; + protected: // Used for creating objects based on type typedef ServerActiveObject* (*Factory) @@ -159,96 +164,5 @@ private: static core::map m_types; }; -class TestSAO : public ServerActiveObject -{ -public: - TestSAO(ServerEnvironment *env, u16 id, v3f pos); - u8 getType() const - {return ACTIVEOBJECT_TYPE_TEST;} - static ServerActiveObject* create(ServerEnvironment *env, u16 id, v3f pos, - const std::string &data); - void step(float dtime, Queue &messages, - bool send_recommended); -private: - float m_timer1; - float m_age; -}; - -class ItemSAO : public ServerActiveObject -{ -public: - ItemSAO(ServerEnvironment *env, u16 id, v3f pos, - const std::string inventorystring); - u8 getType() const - {return ACTIVEOBJECT_TYPE_ITEM;} - static ServerActiveObject* create(ServerEnvironment *env, u16 id, v3f pos, - const std::string &data); - void step(float dtime, Queue &messages, - bool send_recommended); - std::string getClientInitializationData(); - std::string getStaticData(); - InventoryItem* createInventoryItem(); - InventoryItem* createPickedUpItem(){return createInventoryItem();} -private: - std::string m_inventorystring; - v3f m_speed_f; - v3f m_last_sent_position; - IntervalLimiter m_move_interval; -}; - -class RatSAO : public ServerActiveObject -{ -public: - RatSAO(ServerEnvironment *env, u16 id, v3f pos); - u8 getType() const - {return ACTIVEOBJECT_TYPE_RAT;} - static ServerActiveObject* create(ServerEnvironment *env, u16 id, v3f pos, - const std::string &data); - void step(float dtime, Queue &messages, - bool send_recommended); - std::string getClientInitializationData(); - std::string getStaticData(); - InventoryItem* createPickedUpItem(); -private: - bool m_is_active; - IntervalLimiter m_inactive_interval; - v3f m_speed_f; - v3f m_oldpos; - v3f m_last_sent_position; - float m_yaw; - float m_counter1; - float m_counter2; - float m_age; - bool m_touching_ground; -}; - -class Oerkki1SAO : public ServerActiveObject -{ -public: - Oerkki1SAO(ServerEnvironment *env, u16 id, v3f pos); - u8 getType() const - {return ACTIVEOBJECT_TYPE_OERKKI1;} - static ServerActiveObject* create(ServerEnvironment *env, u16 id, v3f pos, - const std::string &data); - void step(float dtime, Queue &messages, - bool send_recommended); - std::string getClientInitializationData(); - std::string getStaticData(); - InventoryItem* createPickedUpItem(){return NULL;} - u16 punch(const std::string &toolname); -private: - bool m_is_active; - IntervalLimiter m_inactive_interval; - v3f m_speed_f; - v3f m_oldpos; - v3f m_last_sent_position; - float m_yaw; - float m_counter1; - float m_counter2; - float m_age; - bool m_touching_ground; - u8 m_hp; -}; - #endif diff --git a/src/test.cpp b/src/test.cpp index 7b86750d8..7d71552a8 100644 --- a/src/test.cpp +++ b/src/test.cpp @@ -31,6 +31,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include "porting.h" #include "content_mapnode.h" +#include "mapsector.h" /* Asserts that the exception occurs @@ -339,6 +340,12 @@ struct TestVoxelManipulator } }; +/* + NOTE: These tests became non-working then NodeContainer was removed. + These should be redone, utilizing some kind of a virtual + interface for Map (IMap would be fine). +*/ +#if 0 struct TestMapBlock { class TC : public NodeContainer @@ -641,13 +648,13 @@ struct TestMapSector // Create one with no heightmaps ServerMapSector sector(&parent, v2s16(1,1)); - EXCEPTION_CHECK(InvalidPositionException, sector.getBlockNoCreate(0)); - EXCEPTION_CHECK(InvalidPositionException, sector.getBlockNoCreate(1)); + assert(sector.getBlockNoCreateNoEx(0) == 0); + assert(sector.getBlockNoCreateNoEx(1) == 0); MapBlock * bref = sector.createBlankBlock(-2); - EXCEPTION_CHECK(InvalidPositionException, sector.getBlockNoCreate(0)); - assert(sector.getBlockNoCreate(-2) == bref); + assert(sector.getBlockNoCreateNoEx(0) == 0); + assert(sector.getBlockNoCreateNoEx(-2) == bref); //TODO: Check for AlreadyExistsException @@ -662,6 +669,7 @@ struct TestMapSector } }; +#endif struct TestSocket { @@ -1028,8 +1036,8 @@ void run_tests() TEST(TestCompress); TEST(TestMapNode); TEST(TestVoxelManipulator); - TEST(TestMapBlock); - TEST(TestMapSector); + //TEST(TestMapBlock); + //TEST(TestMapSector); if(INTERNET_SIMULATOR == false){ TEST(TestSocket); dout_con<<"=== BEGIN RUNNING UNIT TESTS FOR CONNECTION ==="<