diff options
author | Perttu Ahola <celeron55@gmail.com> | 2010-11-29 10:52:07 +0200 |
---|---|---|
committer | Perttu Ahola <celeron55@gmail.com> | 2010-11-29 10:52:07 +0200 |
commit | c707e00195f1035ae535f3fc8697af42e73190c0 (patch) | |
tree | 0e3c2553bb4726821e49dad599f64ecb5b3cec96 /src | |
parent | e8fd5eb8eebbf12b0561d385ef8bc245d87e9ea6 (diff) | |
download | minetest-c707e00195f1035ae535f3fc8697af42e73190c0.tar.gz minetest-c707e00195f1035ae535f3fc8697af42e73190c0.tar.bz2 minetest-c707e00195f1035ae535f3fc8697af42e73190c0.zip |
sitä sun tätä tekeillä, toimii kivasti
Diffstat (limited to 'src')
-rw-r--r-- | src/client.cpp | 24 | ||||
-rw-r--r-- | src/constants.h | 17 | ||||
-rw-r--r-- | src/exceptions.h | 16 | ||||
-rw-r--r-- | src/main.cpp | 348 | ||||
-rw-r--r-- | src/main.h | 3 | ||||
-rw-r--r-- | src/map.cpp | 419 | ||||
-rw-r--r-- | src/map.h | 152 | ||||
-rw-r--r-- | src/mapblock.h | 33 | ||||
-rw-r--r-- | src/mapnode.h | 46 | ||||
-rw-r--r-- | src/mapsector.h | 1 | ||||
-rw-r--r-- | src/server.cpp | 422 | ||||
-rw-r--r-- | src/server.h | 50 | ||||
-rw-r--r-- | src/socket.cpp | 4 | ||||
-rw-r--r-- | src/test.cpp | 42 | ||||
-rw-r--r-- | src/utility.h | 155 |
15 files changed, 981 insertions, 751 deletions
diff --git a/src/client.cpp b/src/client.cpp index a4f0ffb07..cf9feed7d 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -31,7 +31,7 @@ void * ClientUpdateThread::Thread() bool was = m_client->AsyncProcessData(); if(was == false) - sleep_ms(50); + sleep_ms(10); } #if CATCH_UNHANDLED_EXCEPTIONS } @@ -159,13 +159,17 @@ void Client::step(float dtime) { /* Delete unused sectors + + NOTE: This jams the game for a while because deleting sectors + clear caches */ static float counter = -0.001; counter -= dtime; if(counter <= 0.0) { - counter = 10.0; + // 3 minute interval + counter = 180.0; JMutexAutoLock lock(m_env_mutex); @@ -381,6 +385,8 @@ float Client::asyncStep() /*float dtime; { JMutexAutoLock lock1(m_step_dtime_mutex); + if(m_step_dtime < 0.001) + return 0.0; dtime = m_step_dtime; m_step_dtime = 0.0; } @@ -1207,14 +1213,26 @@ bool Client::AsyncProcessPacket(LazyMeshUpdater &mesh_updater) bool Client::AsyncProcessData() { - LazyMeshUpdater mesh_updater(&m_env); for(;;) { + // We want to update the meshes as soon as a single packet has + // been processed + LazyMeshUpdater mesh_updater(&m_env); bool r = AsyncProcessPacket(mesh_updater); if(r == false) break; } return false; + + /* + LazyMeshUpdater mesh_updater(&m_env); + for(;;) + { + bool r = AsyncProcessPacket(mesh_updater); + if(r == false) + break; + } + return false;*/ } void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable) diff --git a/src/constants.h b/src/constants.h index f7ff0aef1..8cc4ee473 100644 --- a/src/constants.h +++ b/src/constants.h @@ -28,19 +28,20 @@ // The absolute working limit is (2^15 - viewing_range). #define MAP_GENERATION_LIMIT (31000) -#define MAX_SIMULTANEOUS_BLOCK_SENDS 2 +//#define MAX_SIMULTANEOUS_BLOCK_SENDS 2 #define FULL_BLOCK_SEND_ENABLE_MIN_TIME_FROM_BUILDING 2.0 -#define LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS 1 +//#define LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS 1 +#define LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS 0 -#define MAX_SIMULTANEOUS_BLOCK_SENDS_SERVER_TOTAL 4 +// Override for the previous one when distance is low +#define BLOCK_SEND_DISABLE_LIMITS_MAX_D 1 -// Viewing range stuff +//#define MAX_SIMULTANEOUS_BLOCK_SENDS_SERVER_TOTAL 4 -#define FPS_DEFAULT_WANTED 30 -#define FPS_DEFAULT_MAX 60 +// Viewing range stuff -#define HEIGHTMAP_RANGE_NODES 300 +//#define HEIGHTMAP_RANGE_NODES 300 //#define FREETIME_RATIO 0.2 #define FREETIME_RATIO 0.15 @@ -56,7 +57,7 @@ //#define ACTIVE_OBJECT_D_BLOCKS 2 // Wether to catch all std::exceptions -#define CATCH_UNJANDLED_EXCEPTIONS 1 +#define CATCH_UNHANDLED_EXCEPTIONS 0 /* Collecting active blocks is stopped after object data diff --git a/src/exceptions.h b/src/exceptions.h index cbe13f1f3..3e939a086 100644 --- a/src/exceptions.h +++ b/src/exceptions.h @@ -81,6 +81,22 @@ public: {} }; +class SettingNotFoundException : public BaseException +{ +public: + SettingNotFoundException(const char *s): + BaseException(s) + {} +}; + +class InvalidFilenameException : public BaseException +{ +public: + InvalidFilenameException(const char *s): + BaseException(s) + {} +}; + /* Some "old-style" interrupts: */ diff --git a/src/main.cpp b/src/main.cpp index 258119c4a..991185ca6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -21,11 +21,15 @@ with this program; if not, write to the Free Software Foundation, Inc., =============================== NOTES ==============================
NOTE: VBO cannot be turned on for fast-changing stuff because there
- is an apparanet memory leak in irrlicht when using it
+ is an apparanet memory leak in irrlicht when using it (not sure)
+
+NOTE: iostream.imbue(std::locale("C")) is very slow
+NOTE: Global locale is now set at initialization
SUGGESTION: add a second lighting value to the MS nibble of param of
air to tell how bright the air node is when there is no sunlight.
When day changes to night, these two values can be interpolated.
+
TODO: Fix address to be ipv6 compatible
TODO: ESC Pause mode in which the cursor is not kept at the center of window.
@@ -93,9 +97,7 @@ TODO: Expose Connection's seqnums and ACKs to server and client. - This also enables server to check if client has received the
most recent block sent, for example.
-SUGG: Add a time value to the param of footstepped grass and check it
- against a global timer when a block is accessed, to make old
- steps fade away.
+TODO: Add a sane bandwidth throttling system to Connection
FIXME: There still are *some* tiny glitches in lighting as seen from
the client side. The server calculates them right but sometimes
@@ -105,8 +107,9 @@ FIXME: There still are *some* tiny glitches in lighting as seen from the sender sends the block as it was before emerging?
TODO: How about adding a "revision" field to MapBlocks?
-TODO: More fine-grained control of client's dumping of blocks from
+SUGG: More fine-grained control of client's dumping of blocks from
memory
+ - ...What does this mean in the first place?
TODO: Somehow prioritize the sending of blocks and combine the block
send queue lengths
@@ -130,9 +133,6 @@ SUGG: Make client send GOTBLOCKS before updating meshes TODO: Server to load starting inventory from disk
-NOTE: iostream.imbue(std::locale("C")) is very slow
-NOTE: Global locale is now set at initialization
-
TODO: PLayers to only be hidden when the client quits.
TODO: - Players to be saved on disk, with inventory
TODO: Players to be saved as text in map/players/<name>
@@ -158,12 +158,18 @@ Block object server side: - A "near blocks" buffer, in which some nearby blocks are stored.
- For all blocks in the buffer, objects are stepped(). This
means they are active.
- - TODO All blocks going in and out of the buffer are recorded.
- - TODO For outgoing blocks, a timestamp is written.
- - TODO For incoming blocks, the time difference is calculated and
+ - TODO: A global active buffer is needed for the server
+ - TODO: All blocks going in and out of the buffer are recorded.
+ - TODO: For outgoing blocks, a timestamp is written.
+ - TODO: For incoming blocks, the time difference is calculated and
objects are stepped according to it.
TODO: A timestamp to blocks
+SUGG: Add a time value to the param of footstepped grass and check it
+ against a global timer when a block is accessed, to make old
+ steps fade away.
+
+
TODO: Add config parameters for server's sending and generating distance
TODO: Copy the text of the last picked sign to inventory in creative
@@ -185,12 +191,16 @@ SUGG: Split MapBlockObject serialization to to-client and to-disk - This will allow saving ages of rats on disk but not sending
them to clients
-TODO: Fix the long-lived Server Block Emerge Jam bug
- - Is it related to the client deleting blocks?
+TODO: Get rid of GotSplitPacketException
+
+Before release:
+
+TODO: Check what goes wrong with caching map to disk (Kray)
Doing now:
======================================================================
+TODO: Implement lighting using VoxelManipulator
======================================================================
@@ -202,7 +212,7 @@ Doing now: the starting place to a static direction.
This allows one to move around with the player and see what
- is actually drawn behind solid things etc.
+ is actually drawn behind solid things and behind the player.
*/
#define FIELD_OF_VIEW_TEST 0
@@ -265,7 +275,8 @@ const char *g_material_filenames[MATERIALS_COUNT] = "../data/tree.png",
"../data/leaves.png",
"../data/grass_footsteps.png",
- "../data/mese.png"
+ "../data/mese.png",
+ "../data/mud.png"
};
video::SMaterial g_materials[MATERIALS_COUNT];
@@ -296,28 +307,39 @@ bool g_viewing_range_all = false; These are loaded from the config file.
*/
-std::string g_dedicated_server;
-
-// Client stuff
-float g_wanted_fps = FPS_DEFAULT_WANTED;
-float g_fps_max = FPS_DEFAULT_MAX;
-s16 g_viewing_range_nodes_max = 300;
-s16 g_viewing_range_nodes_min = 20;
-std::string g_screenW;
-std::string g_screenH;
-std::string g_host_game;
-std::string g_port;
-std::string g_address;
-std::string g_name;
-bool g_random_input = false;
-float g_client_delete_unused_sectors_timeout = 1200;
-
-// Server stuff
-bool g_creative_mode = false;
-HMParams g_hm_params;
-MapParams g_map_params;
-float g_objectdata_interval = 0.2;
-u16 g_active_object_range = 2;
+Settings g_settings;
+
+// Sets default settings
+void set_default_settings()
+{
+ g_settings.set("dedicated_server", "");
+
+ // Client stuff
+ g_settings.set("wanted_fps", "30");
+ g_settings.set("fps_max", "60");
+ g_settings.set("viewing_range_nodes_max", "300");
+ g_settings.set("viewing_range_nodes_min", "20");
+ g_settings.set("screenW", "");
+ g_settings.set("screenH", "");
+ g_settings.set("host_game", "");
+ g_settings.set("port", "");
+ g_settings.set("address", "");
+ g_settings.set("name", "");
+ g_settings.set("random_input", "false");
+ g_settings.set("client_delete_unused_sectors_timeout", "1200");
+
+ // Server stuff
+ g_settings.set("creative_mode", "false");
+ g_settings.set("heightmap_blocksize", "128");
+ g_settings.set("height_randmax", "constant 70.0");
+ g_settings.set("height_randfactor", "constant 0.6");
+ g_settings.set("height_base", "linear 0 35 0");
+ g_settings.set("plants_amount", "1.0");
+ g_settings.set("objectdata_interval", "0.2");
+ g_settings.set("active_object_range", "2");
+ g_settings.set("max_simultaneous_block_sends_per_client", "2");
+ g_settings.set("max_simultaneous_block_sends_server_total", "4");
+}
/*
Random stuff
@@ -354,144 +376,6 @@ std::ostream *derr_server_ptr = &dstream; std::ostream *dout_client_ptr = &dstream;
std::ostream *derr_client_ptr = &dstream;
-/*
- Config stuff
-*/
-
-// Returns false on EOF
-bool parseConfigObject(std::istream &is)
-{
- // float g_wanted_fps
- // s16 g_viewing_range_nodes_max
-
- if(is.eof())
- return false;
-
- std::string line;
- std::getline(is, line);
- //dstream<<"got line: \""<<line<<"\""<<std::endl;
-
- std::string trimmedline = trim(line);
-
- // Ignore comments
- if(trimmedline[0] == '#')
- return true;
-
- //dstream<<"trimmedline=\""<<trimmedline<<"\""<<std::endl;
-
- Strfnd sf(trim(line));
-
- std::string name = sf.next("=");
- name = trim(name);
-
- if(name == "")
- return true;
-
- std::string value = sf.next("\n");
- value = trim(value);
-
- dstream<<"Config name=\""<<name<<"\" value=\""
- <<value<<"\""<<std::endl;
-
- if(name == "dedicated_server")
- g_dedicated_server = value;
-
- // Client stuff
- else if(name == "wanted_fps")
- {
- g_wanted_fps = atof(value.c_str());
- }
- else if(name == "fps_max")
- {
- g_fps_max = atof(value.c_str());
- }
- else if(name == "viewing_range_nodes_max")
- {
- g_viewing_range_nodes_max = stoi(value, 0, 32767);
- }
- else if(name == "viewing_range_nodes_min")
- {
- g_viewing_range_nodes_min = stoi(value, 0, 32767);
- }
- else if(name=="screenW")
- g_screenW = value;
- else if(name=="screenH")
- g_screenH = value;
- else if(name == "host_game")
- g_host_game = value;
- else if(name == "port")
- g_port = value;
- else if(name == "address")
- g_address = value;
- else if(name == "name")
- g_name = value;
- else if(name == "random_input")
- g_random_input = is_yes(value);
- else if(name == "client_delete_unused_sectors_timeout")
- {
- std::istringstream vis(value);
- //vis.imbue(std::locale("C"));
- vis>>g_client_delete_unused_sectors_timeout;
- }
-
- // Server stuff
- else if(name == "creative_mode")
- g_creative_mode = is_yes(value);
- else if(name == "mapgen_heightmap_blocksize")
- {
- s32 d = atoi(value.c_str());
- if(d > 0 && (d & (d-1)) == 0)
- g_hm_params.heightmap_blocksize = d;
- else
- dstream<<"Invalid value in config file: \""
- <<line<<"\""<<std::endl;
- }
- else if(name == "mapgen_height_randmax")
- g_hm_params.height_randmax = value;
- else if(name == "mapgen_height_randfactor")
- g_hm_params.height_randfactor = value;
- else if(name == "mapgen_height_base")
- g_hm_params.height_base = value;
- else if(name == "mapgen_plants_amount")
- {
- std::istringstream vis(value);
- vis>>g_map_params.plants_amount;
- }
- else if(name == "objectdata_inverval")
- {
- std::istringstream vis(value);
- vis>>g_objectdata_interval;
- }
- else if(name == "active_object_range")
- g_active_object_range = stoi(value, 0, 65535);
-
- else
- {
- dstream<<"Unknown option in config file: \""
- <<line<<"\""<<std::endl;
- }
-
- return true;
-}
-
-// Returns true on success
-bool readConfigFile(const char *filename)
-{
- std::ifstream is(filename);
- if(is.good() == false)
- {
- dstream<<DTIME<<"Error opening configuration file: "
- <<filename<<std::endl;
- return false;
- }
-
- dstream<<DTIME<<"Parsing configuration file: "
- <<filename<<std::endl;
-
- while(parseConfigObject(is));
-
- return true;
-}
/*
Timestamp stuff
@@ -875,9 +759,11 @@ void updateViewingRange(f32 frametime, Client *client) // Range_all messes up frametime_avg
if(g_viewing_range_all == true)
return;
+
+ float wanted_fps = g_settings.getFloat("wanted_fps");
// Initialize to the target value
- static float frametime_avg = 1.0/g_wanted_fps;
+ static float frametime_avg = 1.0/wanted_fps;
frametime_avg = frametime_avg * 0.9 + frametime * 0.1;
static f32 counter = 0;
@@ -892,7 +778,7 @@ void updateViewingRange(f32 frametime, Client *client) //float freetime_ratio = 0.4;
float freetime_ratio = FREETIME_RATIO;
- float frametime_wanted = (1.0/(g_wanted_fps/(1.0-freetime_ratio)));
+ float frametime_wanted = (1.0/(wanted_fps/(1.0-freetime_ratio)));
float fraction = sqrt(frametime_avg / frametime_wanted);
@@ -925,11 +811,14 @@ void updateViewingRange(f32 frametime, Client *client) JMutexAutoLock lock(g_range_mutex);
+ s16 viewing_range_nodes_min = g_settings.getS16("viewing_range_nodes_min");
+ s16 viewing_range_nodes_max = g_settings.getS16("viewing_range_nodes_max");
+
s16 n = (float)g_viewing_range_nodes / fraction;
- if(n < g_viewing_range_nodes_min)
- n = g_viewing_range_nodes_min;
- if(n > g_viewing_range_nodes_max)
- n = g_viewing_range_nodes_max;
+ if(n < viewing_range_nodes_min)
+ n = viewing_range_nodes_min;
+ if(n > viewing_range_nodes_max)
+ n = viewing_range_nodes_max;
bool can_change = true;
@@ -1050,10 +939,11 @@ int main(int argc, char *argv[]) disable_stderr = true;
#endif
+ // Initialize debug streams
debugstreams_init(disable_stderr, DEBUGFILE);
+ // Initialize debug stacks
debug_stacks_init();
-
DSTACK(__FUNCTION_NAME);
try
@@ -1063,6 +953,10 @@ int main(int argc, char *argv[]) Basic initialization
*/
+ // Initialize default settings
+ set_default_settings();
+
+ // Print startup message
dstream<<DTIME<<"minetest-c55"
" with SER_FMT_VER_HIGHEST="<<(int)SER_FMT_VER_HIGHEST
<<", ENABLE_TESTS="<<ENABLE_TESTS
@@ -1096,7 +990,7 @@ int main(int argc, char *argv[]) if(argc >= 2)
{
- readConfigFile(argv[1]);
+ g_settings.readConfigFile(argv[1]);
}
else
{
@@ -1108,7 +1002,7 @@ int main(int argc, char *argv[]) for(u32 i=0; i<2; i++)
{
- bool r = readConfigFile(filenames[i]);
+ bool r = g_settings.readConfigFile(filenames[i]);
if(r)
break;
}
@@ -1120,6 +1014,17 @@ int main(int argc, char *argv[]) g_range_mutex.Init();
assert(g_range_mutex.IsInitialized());
+ // Read map parameters from settings
+
+ HMParams hm_params;
+ hm_params.blocksize = g_settings.getU16("heightmap_blocksize");
+ hm_params.randmax = g_settings.get("height_randmax");
+ hm_params.randfactor = g_settings.get("height_randfactor");
+ hm_params.base = g_settings.get("height_base");
+
+ MapParams map_params;
+ map_params.plants_amount = g_settings.getFloat("plants_amount");
+
/*
Ask some stuff
*/
@@ -1127,40 +1032,13 @@ int main(int argc, char *argv[]) std::cout<<std::endl<<std::endl;
char templine[100];
- std::cout<<"Dedicated server? [y = yes]: ";
- if(g_dedicated_server != "")
- {
- std::cout<<g_dedicated_server<<std::endl;
- snprintf(templine, 100, "%s", g_dedicated_server.c_str());
- }
- else
- {
- std::cin.getline(templine, 100);
- }
-
- bool dedicated = false;
- if(templine[0] == 'y')
- dedicated = true;
- if(dedicated)
- std::cout<<"-> yes"<<std::endl;
- else
- std::cout<<"-> no"<<std::endl;
+ // Dedicated?
+ bool dedicated = g_settings.getBoolAsk
+ ("dedicated_server", "Dedicated server?", false);
+ std::cout<<"dedicated = "<<dedicated<<std::endl;
- std::cout<<"Port [empty=30000]: ";
- if(g_port != "")
- {
- std::cout<<g_port<<std::endl;
- snprintf(templine, 100, "%s", g_port.c_str());
- }
- else
- {
- std::cin.getline(templine, 100);
- }
- unsigned short port;
- if(templine[0] == 0)
- port = 30000;
- else
- port = atoi(templine);
+ // Port?
+ u16 port = g_settings.getU16Ask("port", "Port", 30000);
std::cout<<"-> "<<port<<std::endl;
if(dedicated)
@@ -1173,17 +1051,15 @@ int main(int argc, char *argv[]) std::cout<<"========================"<<std::endl;
std::cout<<std::endl;
- Server server("../map", g_creative_mode, g_hm_params,
- g_map_params, g_objectdata_interval,
- g_active_object_range);
+ Server server("../map", hm_params, map_params);
server.start(port);
for(;;)
{
// This is kind of a hack but can be done like this
// because server.step() is very light
- sleep_ms(100);
- server.step(0.1);
+ sleep_ms(30);
+ server.step(0.030);
static int counter = 0;
counter--;
@@ -1214,10 +1090,10 @@ int main(int argc, char *argv[]) char connect_name[100] = "";
std::cout<<"Address to connect to [empty = host a game]: ";
- if(g_address != "" && is_yes(g_host_game) == false)
+ if(g_settings.get("address") != "" && is_yes(g_settings.get("host_game")) == false)
{
- std::cout<<g_address<<std::endl;
- snprintf(connect_name, 100, "%s", g_address.c_str());
+ std::cout<<g_settings.get("address")<<std::endl;
+ snprintf(connect_name, 100, "%s", g_settings.get("address").c_str());
}
else
{
@@ -1235,9 +1111,9 @@ int main(int argc, char *argv[]) std::cout<<"-> "<<connect_name<<std::endl;
char playername[PLAYERNAME_SIZE] = "";
- if(g_name != "")
+ if(g_settings.get("name") != "")
{
- snprintf(playername, PLAYERNAME_SIZE, "%s", g_name.c_str());
+ snprintf(playername, PLAYERNAME_SIZE, "%s", g_settings.get("name").c_str());
}
else
{
@@ -1254,10 +1130,10 @@ int main(int argc, char *argv[]) u16 screenH;
bool fullscreen = false;
- if(g_screenW != "" && g_screenH != "")
+ if(g_settings.get("screenW") != "" && g_settings.get("screenH") != "")
{
- screenW = atoi(g_screenW.c_str());
- screenH = atoi(g_screenH.c_str());
+ screenW = atoi(g_settings.get("screenW").c_str());
+ screenH = atoi(g_settings.get("screenH").c_str());
}
else
{
@@ -1343,7 +1219,7 @@ int main(int argc, char *argv[]) device->setResizable(true);
- if(g_random_input)
+ if(g_settings.getBool("random_input"))
g_input = new RandomInputHandler();
else
g_input = new RealInputHandler(device, &receiver);
@@ -1443,9 +1319,7 @@ int main(int argc, char *argv[]) */
SharedPtr<Server> server;
if(hosting){
- server = new Server("../map", g_creative_mode, g_hm_params,
- g_map_params, g_objectdata_interval,
- g_active_object_range);
+ server = new Server("../map", hm_params, map_params);
server->start(port);
}
@@ -1455,7 +1329,7 @@ int main(int argc, char *argv[]) // TODO: Get rid of the g_materials parameter or it's globalness
Client client(device, g_materials,
- g_client_delete_unused_sectors_timeout,
+ g_settings.getFloat("client_delete_unused_sectors_timeout"),
playername);
Address connect_address(0,0,0,0, port);
@@ -1648,7 +1522,7 @@ int main(int argc, char *argv[]) */
{
- float fps_max = g_fps_max;
+ float fps_max = g_settings.getFloat("fps_max");
u32 frametime_min = 1000./fps_max;
if(busytime_u32 < frametime_min)
@@ -2326,7 +2200,7 @@ int main(int argc, char *argv[]) delete g_input;
/*
- In the end, delete the Irrlicht device.
+ In the end, delete the Irrlicht device.
*/
device->drop();
diff --git a/src/main.h b/src/main.h index 59ef0ff03..950e4039f 100644 --- a/src/main.h +++ b/src/main.h @@ -16,6 +16,9 @@ extern s16 g_viewing_range_nodes; //extern s16 g_actual_viewing_range_nodes; extern bool g_viewing_range_all; +// Settings +extern Settings g_settings; + #include <fstream> // Debug streams diff --git a/src/map.cpp b/src/map.cpp index 35bf8bb40..33f40f064 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -9,6 +9,7 @@ #include "client.h" #include "filesys.h" #include "utility.h" +#include "voxel.h" #ifdef _WIN32 #include <windows.h> @@ -18,6 +19,47 @@ #define sleep_ms(x) usleep(x*1000) #endif +MapBlockPointerCache::MapBlockPointerCache(Map *map) +{ + m_map = map; + m_map->m_blockcachelock.cacheCreated(); + + m_from_cache_count = 0; + m_from_map_count = 0; +} + +MapBlockPointerCache::~MapBlockPointerCache() +{ + m_map->m_blockcachelock.cacheRemoved(); + + dstream<<"MapBlockPointerCache:" + <<" from_cache_count="<<m_from_cache_count + <<" from_map_count="<<m_from_map_count + <<std::endl; +} + +MapBlock * MapBlockPointerCache::getBlockNoCreate(v3s16 p) +{ + core::map<v3s16, MapBlock*>::Node *n = NULL; + n = m_blocks.find(p); + if(n != NULL) + { + m_from_cache_count++; + return n->getValue(); + } + + m_from_map_count++; + + // Throws InvalidPositionException if not found + MapBlock *b = m_map->getBlockNoCreate(p); + m_blocks[p] = b; + return b; +} + +/* + Map +*/ + Map::Map(std::ostream &dout): m_dout(dout), m_camera_position(0,0,0), @@ -158,33 +200,11 @@ bool Map::isNodeUnderground(v3s16 p) } } -#ifdef LKJnb -//TODO: Remove: Not used. -/* - Goes recursively through the neighbours of the node. - - Alters only transparent nodes. - - If the lighting of the neighbour is lower than the lighting of - the node was (before changing it to 0 at the step before), the - lighting of the neighbour is set to 0 and then the same stuff - repeats for the neighbour. - - Some things are made strangely to make it as fast as possible. - - Usage: (for clearing all possible spreaded light of a lamp) - NOTE: This is outdated - core::list<v3s16> light_sources; - core::map<v3s16, MapBlock*> modified_blocks; - u8 oldlight = node_at_pos.light; - node_at_pos.setLight(0); - unLightNeighbors(pos, oldlight, light_sources, modified_blocks); -*/ -void Map::unLightNeighbors(v3s16 pos, u8 oldlight, - core::map<v3s16, bool> & light_sources, - core::map<v3s16, MapBlock*> & modified_blocks) +#if 0 +void Map::interpolate(v3s16 block, + core::map<v3s16, MapBlock*> & modified_blocks) { - v3s16 dirs[6] = { + const v3s16 dirs[6] = { v3s16(0,0,1), // back v3s16(0,1,0), // top v3s16(1,0,0), // right @@ -192,7 +212,16 @@ void Map::unLightNeighbors(v3s16 pos, u8 oldlight, v3s16(0,-1,0), // bottom v3s16(-1,0,0), // left }; + + if(from_nodes.size() == 0) + return; + u32 blockchangecount = 0; + + core::map<v3s16, bool> lighted_nodes; + core::map<v3s16, bool>::Iterator j; + j = from_nodes.getIterator(); + /* Initialize block cache */ @@ -201,23 +230,22 @@ void Map::unLightNeighbors(v3s16 pos, u8 oldlight, // Cache this a bit, too bool block_checked_in_modified = false; - // Loop through 6 neighbors - for(u16 i=0; i<6; i++){ - // Get the position of the neighbor node - v3s16 n2pos = pos + dirs[i]; + for(; j.atEnd() == false; j++) + //for(; j != from_nodes.end(); j++) + { + v3s16 pos = j.getNode()->getKey(); + //v3s16 pos = *j; + //dstream<<"pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl; + v3s16 blockpos = getNodeBlockPos(pos); - // Get the block where the node is located - v3s16 blockpos = getNodeBlockPos(n2pos); - // Only fetch a new block if the block position has changed try{ - if(block == NULL || blockpos != blockpos_last) - { + if(block == NULL || blockpos != blockpos_last){ block = getBlockNoCreate(blockpos); blockpos_last = blockpos; - + block_checked_in_modified = false; - //blockchangecount++; + blockchangecount++; } } catch(InvalidPositionException &e) @@ -227,33 +255,75 @@ void Map::unLightNeighbors(v3s16 pos, u8 oldlight, if(block->isDummy()) continue; - + // Calculate relative position in block - v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE; + v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE; + // Get node straight from the block - MapNode n2 = block->getNode(relpos); - - /* - If the neighbor is dimmer than what was specified - as oldlight (the light of the previous node) - */ - if(n2.getLight() < oldlight) - { - /* - And the neighbor is transparent and it has some light - */ - if(n2.light_propagates() && n2.getLight() != 0) + MapNode n = block->getNode(relpos); + + u8 oldlight = n.getLight(); + u8 newlight = diminish_light(oldlight); + + // Loop through 6 neighbors + for(u16 i=0; i<6; i++){ + // Get the position of the neighbor node + v3s16 n2pos = pos + dirs[i]; + + // Get the block where the node is located + v3s16 blockpos = getNodeBlockPos(n2pos); + + try { + // Only fetch a new block if the block position has changed + try{ + if(block == NULL || blockpos != blockpos_last){ + block = getBlockNoCreate(blockpos); + blockpos_last = blockpos; + + block_checked_in_modified = false; + blockchangecount++; + } + } + catch(InvalidPositionException &e) + { + continue; + } + + // Calculate relative position in block + v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE; + // Get node straight from the block + MapNode n2 = block->getNode(relpos); + + bool changed = false; /* - Set light to 0 and recurse. + If the neighbor is brighter than the current node, + add to list (it will light up this node on its turn) */ - u8 current_light = n2.getLight(); - n2.setLight(0); - block->setNode(relpos, n2); - unLightNeighbors(n2pos, current_light, - light_sources, modified_blocks); - - if(block_checked_in_modified == false) + if(n2.getLight() > undiminish_light(oldlight)) + { + lighted_nodes.insert(n2pos, true); + //lighted_nodes.push_back(n2pos); + changed = true; + } + /* + If the neighbor is dimmer than how much light this node + would spread on it, add to list + */ + if(n2.getLight() < newlight) + { + if(n2.light_propagates()) + { + n2.setLight(newlight); + block->setNode(relpos, n2); + lighted_nodes.insert(n2pos, true); + //lighted_nodes.push_back(n2pos); + changed = true; + } + } + + // Add to modified_blocks + if(changed == true && block_checked_in_modified == false) { // If the block is not found in modified_blocks, add. if(modified_blocks.find(blockpos) == NULL) @@ -263,12 +333,20 @@ void Map::unLightNeighbors(v3s16 pos, u8 oldlight, block_checked_in_modified = true; } } - } - else{ - //light_sources.push_back(n2pos); - light_sources.insert(n2pos, true); + catch(InvalidPositionException &e) + { + continue; + } } } + + /*dstream<<"spreadLight(): Changed block " + <<blockchangecount<<" times" + <<" for "<<from_nodes.size()<<" nodes" + <<std::endl;*/ + + if(lighted_nodes.size() > 0) + spreadLight(lighted_nodes, modified_blocks); } #endif @@ -1090,6 +1168,13 @@ void Map::timerUpdate(float dtime) void Map::deleteSectors(core::list<v2s16> &list, bool only_blocks) { + /* + Wait for caches to be removed before continuing. + + This disables the existence of caches while locked + */ + SharedPtr<JMutexAutoLock> cachelock(m_blockcachelock.waitCaches()); + core::list<v2s16>::Iterator j; for(j=list.begin(); j!=list.end(); j++) { @@ -1215,13 +1300,13 @@ ServerMap::ServerMap(std::string savedir, HMParams hmp, MapParams mp): // Create master heightmap ValueGenerator *maxgen = - ValueGenerator::deSerialize(hmp.height_randmax); + ValueGenerator::deSerialize(hmp.randmax); ValueGenerator *factorgen = - ValueGenerator::deSerialize(hmp.height_randfactor); + ValueGenerator::deSerialize(hmp.randfactor); ValueGenerator *basegen = - ValueGenerator::deSerialize(hmp.height_base); + ValueGenerator::deSerialize(hmp.base); m_heightmap = new UnlimitedHeightmap - (hmp.heightmap_blocksize, maxgen, factorgen, basegen); + (hmp.blocksize, maxgen, factorgen, basegen); // Set map parameters m_params = mp; @@ -1409,6 +1494,9 @@ MapSector * ServerMap::emergeSector(v2s16 p2d) SECTOR_OBJECT_TREE_1); } } + /* + Plant some bushes if sector is pit-like + */ { // Pitness usually goes at around -0.5...0.5 u32 bush_max = 0; @@ -1429,6 +1517,22 @@ MapSector * ServerMap::emergeSector(v2s16 p2d) SECTOR_OBJECT_BUSH_1); } } + /* + Add ravine (randomly) + */ + { + if(rand()%10 == 0) + { + s16 s = 6; + s16 x = rand()%(MAP_BLOCKSIZE-s*2-1)+s; + s16 z = rand()%(MAP_BLOCKSIZE-s*2-1)+s; + /*s16 x = 8; + s16 z = 8;*/ + s16 y = sector->getGroundHeight(v2s16(x,z))+1; + objects->insert(v3s16(x, y, z), + SECTOR_OBJECT_RAVINE); + } + } /* Insert to container @@ -1533,9 +1637,16 @@ MapBlock * ServerMap::emergeBlock( } // Randomize a bit. This makes dungeons. - bool low_block_is_empty = false; + /*bool low_block_is_empty = false; if(rand() % 4 == 0) - low_block_is_empty = true; + low_block_is_empty = true;*/ + + s32 ued = 4; + bool underground_emptiness[ued*ued*ued]; + for(s32 i=0; i<ued*ued*ued; i++) + { + underground_emptiness[i] = ((rand() % 4) == 0); + } // This is the basic material of what the visible flat ground // will consist of @@ -1551,9 +1662,7 @@ MapBlock * ServerMap::emergeBlock( { //dstream<<"emergeBlock: x0="<<x0<<", z0="<<z0<<std::endl; float surface_y_f = sector->getGroundHeight(v2s16(x0,z0)); - assert(surface_y_f > GROUNDHEIGHT_VALID_MINVALUE); - s16 surface_y = surface_y_f; //avg_ground_y += surface_y; if(surface_y < lowest_ground_y) @@ -1574,13 +1683,14 @@ MapBlock * ServerMap::emergeBlock( else surface_depth = (1.-(slope-min_slope)/max_slope) * min_slope_depth; - for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++){ + for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++) + { s16 real_y = block_y * MAP_BLOCKSIZE + y0; MapNode n; /* Calculate lighting - FIXME: If there are some man-made structures above the + NOTE: If there are some man-made structures above the newly created block, they won't be taken into account. */ if(real_y > surface_y) @@ -1589,12 +1699,17 @@ MapBlock * ServerMap::emergeBlock( Calculate material */ // If node is very low - if(real_y <= surface_y - 10){ + if(real_y <= surface_y - 7){ // Create dungeons - if(low_block_is_empty){ + if(underground_emptiness[ + ued*ued*(z0*ued/MAP_BLOCKSIZE) + +ued*(y0*ued/MAP_BLOCKSIZE) + +(x0*ued/MAP_BLOCKSIZE)]) + { n.d = MATERIAL_AIR; } - else{ + else + { n.d = MATERIAL_STONE; } } @@ -1603,7 +1718,14 @@ MapBlock * ServerMap::emergeBlock( n.d = MATERIAL_STONE; // If node is at or under heightmap y else if(real_y <= surface_y) - n.d = material; + { + // If under water level, it's mud + if(real_y < WATER_LEVEL) + n.d = MATERIAL_MUD; + // Else it's the main material + else + n.d = material; + } // If node is over heightmap y else{ // If under water level, it's water @@ -1629,10 +1751,20 @@ MapBlock * ServerMap::emergeBlock( block->setIsUnderground(is_underground); /* + Force lighting update if underground. + This is needed because of ravines. + */ + + if(is_underground) + { + lighting_invalidated_blocks[block->getPos()] = block; + } + + /* Add some minerals */ - if(is_underground && low_block_is_empty == false) + if(is_underground) { s16 underground_level = lowest_ground_y/MAP_BLOCKSIZE - block_y; for(s16 i=0; i<underground_level*3; i++) @@ -1640,9 +1772,6 @@ MapBlock * ServerMap::emergeBlock( if(rand()%2 == 0) { v3s16 cp( - /*(rand()%(MAP_BLOCKSIZE-4))+2, - (rand()%(MAP_BLOCKSIZE-4))+2, - (rand()%(MAP_BLOCKSIZE-4))+2*/ (rand()%(MAP_BLOCKSIZE-2))+1, (rand()%(MAP_BLOCKSIZE-2))+1, (rand()%(MAP_BLOCKSIZE-2))+1 @@ -1656,6 +1785,9 @@ MapBlock * ServerMap::emergeBlock( for(u16 i=0; i<26; i++) { + if(!is_ground_material(block->getNode(cp+g_26dirs[i]).d)) + continue; + if(rand()%8 == 0) block->setNode(cp+g_26dirs[i], n); } @@ -1666,7 +1798,7 @@ MapBlock * ServerMap::emergeBlock( /* Create a few rats in empty blocks underground */ - if(is_underground && low_block_is_empty == true) + /*if(is_underground && low_block_is_empty == true) { //for(u16 i=0; i<2; i++) { @@ -1674,42 +1806,54 @@ MapBlock * ServerMap::emergeBlock( RatObject *obj = new RatObject(NULL, -1, intToFloat(pos)); block->addObject(obj); } - } + }*/ /* - TODO: REMOVE - DEBUG - Add some objects to the block for testing. + Add block to sector. */ - /*if(p == v3s16(0,0,0)) - { - //TestObject *obj = new TestObject(NULL, -1, v3f(BS*8,BS*8,BS*8)); - Test2Object *obj = new Test2Object(NULL, -1, v3f(BS*8,BS*15,BS*8)); - block->addObject(obj); - }*/ + sector->insertBlock(block); /* + Do some interpolation for dungeons + */ + +#if 0 { - v3s16 pos(8, 11, 8); - SignObject *obj = new SignObject(NULL, -1, intToFloat(pos)); - obj->setText("Moicka"); - obj->setYaw(45); - block->addObject(obj); - } + TimeTaker timer("interpolation", g_device); + + MapVoxelManipulator vmanip(this); + + v3s16 relpos = block->getPosRelative(); + + vmanip.interpolate(VoxelArea(relpos-v3s16(1,1,1), + relpos+v3s16(1,1,1)*(MAP_BLOCKSIZE+1))); + /*vmanip.interpolate(VoxelArea(relpos, + relpos+v3s16(1,1,1)*(MAP_BLOCKSIZE-1)));*/ + + core::map<v3s16, MapBlock*> modified_blocks; + vmanip.blitBack(modified_blocks); + dstream<<"blitBack modified "<<modified_blocks.size() + <<" blocks"<<std::endl; + // Add modified blocks to changed_blocks and lighting_invalidated_blocks + for(core::map<v3s16, MapBlock*>::Iterator + i = modified_blocks.getIterator(); + i.atEnd() == false; i++) { - v3s16 pos(8, 11, 8); - RatObject *obj = new RatObject(NULL, -1, intToFloat(pos)); - block->addObject(obj); + MapBlock *block = i.getNode()->getValue(); + + changed_blocks.insert(block->getPos(), block); + //lighting_invalidated_blocks.insert(block->getPos(), block); } - */ + + } +#endif /* - Add block to sector. + Sector object stuff */ - sector->insertBlock(block); - - // An y-wise container if changed blocks + + // An y-wise container of changed blocks core::map<s16, MapBlock*> changed_blocks_sector; /* @@ -1722,6 +1866,7 @@ MapBlock * ServerMap::emergeBlock( i.atEnd() == false; i++) { v3s16 p = i.getNode()->getKey(); + v2s16 p2d(p.X,p.Z); u8 d = i.getNode()->getValue(); //v3s16 p = p_sector - v3s16(0, block_y*MAP_BLOCKSIZE, 0); @@ -1795,6 +1940,66 @@ MapBlock * ServerMap::emergeBlock( objects_to_remove.push_back(p); } } + else if(d == SECTOR_OBJECT_RAVINE) + { + s16 maxdepth = -20; + v3s16 p_min = p + v3s16(-6,maxdepth,-6); + v3s16 p_max = p + v3s16(6,6,6); + if(sector->isValidArea(p_min, p_max, + &changed_blocks_sector)) + { + MapNode n; + n.d = MATERIAL_STONE; + MapNode n2; + n2.d = MATERIAL_AIR; + s16 depth = maxdepth + (rand()%10); + s16 z = 0; + s16 minz = -6 - (-2); + s16 maxz = 6 -1; + for(s16 x=-6; x<=6; x++) + { + z += -1 + (rand()%3); + if(z < minz) + z = minz; + if(z > maxz) + z = maxz; + for(s16 y=depth+(rand()%2); y<=6; y++) + { + /*std::cout<<"("<<p2.X<<","<<p2.Y<<","<<p2.Z<<")" + <<std::endl;*/ + { + v3s16 p2 = p + v3s16(x,y,z-2); + if(is_ground_material(sector->getNode(p2).d)) + sector->setNode(p2, n); + } + { + v3s16 p2 = p + v3s16(x,y,z-1); + if(is_ground_material(sector->getNode(p2).d)) + sector->setNode(p2, n2); + } + { + v3s16 p2 = p + v3s16(x,y,z+0); + if(is_ground_material(sector->getNode(p2).d)) + sector->setNode(p2, n2); + } + { + v3s16 p2 = p + v3s16(x,y,z+1); + if(is_ground_material(sector->getNode(p2).d)) + sector->setNode(p2, n); + } + + //if(sector->getNode(p+v3s16(x,y,z+1)).solidness()==2) + //if(p.Y+y <= sector->getGroundHeight(p2d+v2s16(x,z-2))+0.5) + } + } + + objects_to_remove.push_back(p); + + // Lighting has to be recalculated for this one. + sector->getBlocksInArea(p_min, p_max, + lighting_invalidated_blocks); + } + } else { dstream<<"ServerMap::emergeBlock(): " @@ -1807,7 +2012,7 @@ MapBlock * ServerMap::emergeBlock( { dstream<<"WARNING: "<<__FUNCTION_NAME <<": while inserting object "<<(int)d - <<" to ("<<p.X<<","<<p.Y<<","<<p.Z<<")" + <<" to ("<<p.X<<","<<p.Y<<","<<p.Z<<"):" <<" InvalidPositionException.what()=" <<e.what()<<std::endl; // This is not too fatal and seems to happen sometimes. @@ -26,12 +26,129 @@ #include "mapsector.h" #include "constants.h" -class InvalidFilenameException : public BaseException +class Map; + +/* + A cache for short-term fast access to map data + + NOTE: This doesn't really make anything more efficient + NOTE: Use VoxelManipulator, if possible + TODO: Get rid of this? +*/ +class MapBlockPointerCache : public NodeContainer +{ +public: + MapBlockPointerCache(Map *map); + ~MapBlockPointerCache(); + + virtual u16 nodeContainerId() const + { + return NODECONTAINER_ID_MAPBLOCKCACHE; + } + + MapBlock * getBlockNoCreate(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; + } + + // virtual from NodeContainer + MapNode getNode(v3s16 p) + { + v3s16 blockpos = getNodeBlockPos(p); + MapBlock * blockref = getBlockNoCreate(blockpos); + v3s16 relpos = p - blockpos*MAP_BLOCKSIZE; + + return blockref->getNodeNoCheck(relpos); + } + + // virtual from NodeContainer + void setNode(v3s16 p, MapNode & n) + { + v3s16 blockpos = getNodeBlockPos(p); + MapBlock * block = getBlockNoCreate(blockpos); + v3s16 relpos = p - blockpos*MAP_BLOCKSIZE; + block->setNodeNoCheck(relpos, n); + m_modified_blocks[blockpos] = block; + } + + core::map<v3s16, MapBlock*> m_modified_blocks; + +private: + Map *m_map; + core::map<v3s16, MapBlock*> m_blocks; + + u32 m_from_cache_count; + u32 m_from_map_count; +}; + +class CacheLock { public: - InvalidFilenameException(const char *s): - BaseException(s) - {} + CacheLock() + { + m_count = 0; + m_count_mutex.Init(); + m_cache_mutex.Init(); + m_waitcache_mutex.Init(); + } + + void cacheCreated() + { + JMutexAutoLock waitcachelock(m_waitcache_mutex); + JMutexAutoLock countlock(m_count_mutex); + + // If this is the first cache, grab the cache lock + if(m_count == 0) + m_cache_mutex.Lock(); + + m_count++; + } + + void cacheRemoved() + { + JMutexAutoLock countlock(m_count_mutex); + + assert(m_count > 0); + + m_count--; + + // If this is the last one, release the cache lock + if(m_count == 0) + m_cache_mutex.Unlock(); + } + + /* + This lock should be taken when removing stuff that can be + pointed by the cache. + + You'll want to grab this in a SharedPtr. + */ + JMutexAutoLock * waitCaches() + { + JMutexAutoLock waitcachelock(m_waitcache_mutex); + return new JMutexAutoLock(m_cache_mutex); + } + +private: + // Count of existing caches + u32 m_count; + JMutex m_count_mutex; + // This is locked always when there are some caches + JMutex m_cache_mutex; + // Locked so that when waitCaches() is called, no more caches are created + JMutex m_waitcache_mutex; }; #define MAPTYPE_BASE 0 @@ -60,6 +177,13 @@ protected: public: v3s16 drawoffset; // for drawbox() + + /* + Used by MapBlockPointerCache. + + waitCaches() can be called to remove all caches before continuing + */ + CacheLock m_blockcachelock; Map(std::ostream &dout); virtual ~Map(); @@ -154,7 +278,7 @@ public: MapBlock * blockref = getBlockNoCreate(blockpos); v3s16 relpos = p - blockpos*MAP_BLOCKSIZE; - return blockref->getNode(relpos); + return blockref->getNodeNoCheck(relpos); } // virtual from NodeContainer @@ -163,7 +287,7 @@ public: v3s16 blockpos = getNodeBlockPos(p); MapBlock * blockref = getBlockNoCreate(blockpos); v3s16 relpos = p - blockpos*MAP_BLOCKSIZE; - blockref->setNode(relpos, n); + blockref->setNodeNoCheck(relpos, n); } /*MapNode getNodeGenerate(v3s16 p) @@ -247,15 +371,15 @@ struct HMParams { HMParams() { - heightmap_blocksize = 64; - height_randmax = "constant 70.0"; - height_randfactor = "constant 0.6"; - height_base = "linear 0 80 0"; + blocksize = 64; + randmax = "constant 70.0"; + randfactor = "constant 0.6"; + base = "linear 0 80 0"; } - s16 heightmap_blocksize; - std::string height_randmax; - std::string height_randfactor; - std::string height_base; + s16 blocksize; + std::string randmax; + std::string randfactor; + std::string base; }; // Map parameters diff --git a/src/mapblock.h b/src/mapblock.h index 60f78b6ff..48f877551 100644 --- a/src/mapblock.h +++ b/src/mapblock.h @@ -38,7 +38,9 @@ enum { NODECONTAINER_ID_MAPBLOCK, NODECONTAINER_ID_MAPSECTOR, - NODECONTAINER_ID_MAP + NODECONTAINER_ID_MAP, + NODECONTAINER_ID_MAPBLOCKCACHE, + NODECONTAINER_ID_VOXELMANIPULATOR, }; class NodeContainer @@ -246,6 +248,35 @@ public: } /* + Non-checking variants of the above + */ + + MapNode getNodeNoCheck(s16 x, s16 y, s16 z) + { + if(data == NULL) + throw InvalidPositionException(); + return data[z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + y*MAP_BLOCKSIZE + x]; + } + + MapNode getNodeNoCheck(v3s16 p) + { + return getNodeNoCheck(p.X, p.Y, p.Z); + } + + void setNodeNoCheck(s16 x, s16 y, s16 z, MapNode & n) + { + if(data == NULL) + throw InvalidPositionException(); + data[z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + y*MAP_BLOCKSIZE + x] = n; + setChangedFlag(); + } + + void setNodeNoCheck(v3s16 p, MapNode & n) + { + setNodeNoCheck(p.X, p.Y, p.Z, n); + } + + /* These functions consult the parent container if the position is not valid on this MapBlock. */ diff --git a/src/mapnode.h b/src/mapnode.h index 68e669161..981f36d86 100644 --- a/src/mapnode.h +++ b/src/mapnode.h @@ -17,10 +17,25 @@ #define MATERIALS_COUNT 256 -// This is completely ignored. It doesn't create faces with anything. +/* + Ignored node. + + param is used for custom information in special containers, + like VoxelManipulator. + + Anything that stores MapNodes doesn't have to preserve parameters + associated with this material. + + Doesn't create faces with anything and is considered being + out-of-map in the game map. +*/ #define MATERIAL_IGNORE 255 -// This is the common material through which the player can walk -// and which is transparent to light +#define MATERIAL_IGNORE_DEFAULT_PARAM 0 + +/* + The common material through which the player can walk and which + is transparent to light +*/ #define MATERIAL_AIR 254 /* @@ -63,6 +78,8 @@ enum Material MATERIAL_GRASS_FOOTSTEPS, MATERIAL_MESE, + + MATERIAL_MUD, // This is set to the number of the actual values in this enum USEFUL_MATERIAL_COUNT @@ -126,6 +143,21 @@ inline u8 face_materials(u8 m1, u8 m2) return 2; } +/* + Returns true for materials that form the base ground that + follows the main heightmap +*/ +inline bool is_ground_material(u8 m) +{ + return( + m == MATERIAL_STONE || + m == MATERIAL_GRASS || + m == MATERIAL_GRASS_FOOTSTEPS || + m == MATERIAL_MESE || + m == MATERIAL_MUD + ); +} + struct MapNode { //TODO: block type to differ from material @@ -133,9 +165,6 @@ struct MapNode // block type u8 d; - // Removed because light is now stored in param for air - // f32 light; - /* Misc parameter. Initialized to 0. - For light_propagates() blocks, this is light intensity, @@ -155,6 +184,11 @@ struct MapNode param = a_param; } + bool operator==(const MapNode &other) + { + return (d == other.d && param == other.param); + } + bool light_propagates() { return light_propagates_material(d); diff --git a/src/mapsector.h b/src/mapsector.h index 196a129c3..de8cab536 100644 --- a/src/mapsector.h +++ b/src/mapsector.h @@ -20,6 +20,7 @@ #define SECTOR_OBJECT_TEST 0 #define SECTOR_OBJECT_TREE_1 1 #define SECTOR_OBJECT_BUSH_1 2 +#define SECTOR_OBJECT_RAVINE 3 #define MAPSECTOR_FIXEDHEIGHTMAPS_MAXCOUNT 4 diff --git a/src/server.cpp b/src/server.cpp index 83d43599f..e17e21d32 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -178,7 +178,10 @@ void * EmergeThread::Thread() modified_blocks.insert(block->getPos(), block); } - //TimeTaker timer("** updateLighting", g_device); + /*dstream<<"lighting "<<lighting_invalidated_blocks.size() + <<" blocks"<<std::endl; + TimeTaker timer("** updateLighting", g_device);*/ + // Update lighting without locking the environment mutex, // add modified blocks to changed blocks map.updateLighting(lighting_invalidated_blocks, modified_blocks); @@ -222,11 +225,11 @@ void * EmergeThread::Thread() client->SetBlocksNotSent(modified_blocks); } - if(q->peer_ids.find(client->peer_id) != NULL) + /*if(q->peer_ids.find(client->peer_id) != NULL) { // Decrement emerge queue count of client client->BlockEmerged(); - } + }*/ } } @@ -246,282 +249,6 @@ void * EmergeThread::Thread() return NULL; } -#if 0 -void RemoteClient::SendBlocks(Server *server, float dtime) -{ - DSTACK(__FUNCTION_NAME); - - /* - Find what blocks to send to the client next, and send them. - - Throttling is based on limiting the amount of blocks "flying" - at a given time. - */ - - // Can't send anything without knowing version - if(serialization_version == SER_FMT_VER_INVALID) - { - dstream<<"RemoteClient::SendBlocks(): Not sending, no version." - <<std::endl; - return; - } - - { - JMutexAutoLock lock(m_blocks_sending_mutex); - - if(m_blocks_sending.size() >= MAX_SIMULTANEOUS_BLOCK_SENDS) - { - //dstream<<"Not sending any blocks, Queue full."<<std::endl; - return; - } - } - - Player *player = server->m_env.getPlayer(peer_id); - - v3f playerpos = player->getPosition(); - v3f playerspeed = player->getSpeed(); - - v3s16 center_nodepos = floatToInt(playerpos); - - v3s16 center = getNodeBlockPos(center_nodepos); - - /* - Get the starting value of the block finder radius. - */ - s16 last_nearest_unsent_d; - s16 d_start; - { - JMutexAutoLock lock(m_blocks_sent_mutex); - - if(m_last_center != center) - { - m_nearest_unsent_d = 0; - m_last_center = center; - } - - static float reset_counter = 0; - reset_counter += dtime; - if(reset_counter > 5.0) - { - reset_counter = 0; - m_nearest_unsent_d = 0; - } - - last_nearest_unsent_d = m_nearest_unsent_d; - - d_start = m_nearest_unsent_d; - } - - u16 maximum_simultaneous_block_sends = MAX_SIMULTANEOUS_BLOCK_SENDS; - - { - SharedPtr<JMutexAutoLock> lock(m_time_from_building.getLock()); - m_time_from_building.m_value += dtime; - /* - Check the time from last addNode/removeNode. - Decrease send rate if player is building stuff. - */ - if(m_time_from_building.m_value - < FULL_BLOCK_SEND_ENABLE_MIN_TIME_FROM_BUILDING) - { - maximum_simultaneous_block_sends - = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS; - } - } - - // Serialization version used - //u8 ser_version = serialization_version; - - //bool has_incomplete_blocks = false; - - /* - TODO: Get this from somewhere - */ - //s16 d_max = 7; - s16 d_max = 8; - - //TODO: Get this from somewhere (probably a bigger value) - s16 d_max_gen = 5; - - //dstream<<"Starting from "<<d_start<<std::endl; - - for(s16 d = d_start; d <= d_max; d++) - { - //dstream<<"RemoteClient::SendBlocks(): d="<<d<<std::endl; - - //if(has_incomplete_blocks == false) - { - JMutexAutoLock lock(m_blocks_sent_mutex); - /* - If m_nearest_unsent_d was changed by the EmergeThread - (it can change it to 0 through SetBlockNotSent), - update our d to it. - Else update m_nearest_unsent_d - */ - if(m_nearest_unsent_d != last_nearest_unsent_d) - { - d = m_nearest_unsent_d; - } - else - { - m_nearest_unsent_d = d; - } - last_nearest_unsent_d = m_nearest_unsent_d; - } - - /* - Get the border/face dot coordinates of a "d-radiused" - box - */ - core::list<v3s16> list; - getFacePositions(list, d); - - core::list<v3s16>::Iterator li; - for(li=list.begin(); li!=list.end(); li++) - { - v3s16 p = *li + center; - - /* - Send throttling - - Don't allow too many simultaneous transfers - - Also, don't send blocks that are already flying. - */ - { - JMutexAutoLock lock(m_blocks_sending_mutex); - - if(m_blocks_sending.size() - >= maximum_simultaneous_block_sends) - { - /*dstream<<"Not sending more blocks. Queue full. " - <<m_blocks_sending.size() - <<std::endl;*/ - return; - } - - if(m_blocks_sending.find(p) != NULL) - continue; - } - - /* - Do not go over-limit - */ - if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE - || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE - || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE - || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE - || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE - || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE) - continue; - - bool generate = d <= d_max_gen; - - // Limit the generating area vertically to half - if(abs(p.Y - center.Y) > d_max_gen / 2) - generate = false; - - /* - Don't send already sent blocks - */ - { - JMutexAutoLock lock(m_blocks_sent_mutex); - - if(m_blocks_sent.find(p) != NULL) - continue; - } - - /* - Check if map has this block - */ - MapBlock *block = NULL; - try - { - block = server->m_env.getMap().getBlockNoCreate(p); - } - catch(InvalidPositionException &e) - { - } - - bool surely_not_found_on_disk = false; - if(block != NULL) - { - /*if(block->isIncomplete()) - { - has_incomplete_blocks = true; - continue; - }*/ - - if(block->isDummy()) - { - surely_not_found_on_disk = true; - } - } - - /* - If block has been marked to not exist on disk (dummy) - and generating new ones is not wanted, skip block. TODO - */ - if(generate == false && surely_not_found_on_disk == true) - { - // get next one. - continue; - } - - /* - Add inexistent block to emerge queue. - */ - if(block == NULL || surely_not_found_on_disk) - { - // Block not found. - SharedPtr<JMutexAutoLock> lock - (m_num_blocks_in_emerge_queue.getLock()); - - //TODO: Get value from somewhere - //TODO: Balance between clients - //if(server->m_emerge_queue.size() < 1) - - // Allow only one block in emerge queue - if(m_num_blocks_in_emerge_queue.m_value == 0) - { - // Add it to the emerge queue and trigger the thread - - u8 flags = 0; - if(generate == false) - flags |= TOSERVER_GETBLOCK_FLAG_OPTIONAL; - - { - m_num_blocks_in_emerge_queue.m_value++; - } - - server->m_emerge_queue.addBlock(peer_id, p, flags); - server->m_emergethread.trigger(); - } - - // get next one. - continue; - } - - /* - Send block - */ - - /*dstream<<"RemoteClient::SendBlocks(): d="<<d<<", p=" - <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")" - <<" sending queue size: "<<m_blocks_sending.size()<<std::endl;*/ - - server->SendBlockNoLock(peer_id, block, serialization_version); - - /* - Add to history - */ - SentBlock(p); - } - } - - // Don't add anything here. The loop breaks by returning. -} -#endif // backup of SendBlocks - void RemoteClient::GetNextBlocks(Server *server, float dtime, core::array<PrioritySortedBlockTransfer> &dest) { @@ -531,7 +258,8 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime, { JMutexAutoLock lock(m_blocks_sending_mutex); - if(m_blocks_sending.size() >= MAX_SIMULTANEOUS_BLOCK_SENDS) + if(m_blocks_sending.size() >= g_settings.getU16 + ("max_simultaneous_block_sends_per_client")) { //dstream<<"Not sending any blocks, Queue full."<<std::endl; return; @@ -574,15 +302,17 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime, d_start = m_nearest_unsent_d; } - u16 maximum_simultaneous_block_sends = MAX_SIMULTANEOUS_BLOCK_SENDS; + u16 maximum_simultaneous_block_sends = g_settings.getU16 + ("max_simultaneous_block_sends_per_client"); + /* + Check the time from last addNode/removeNode. + + Decrease send rate if player is building stuff. + */ { SharedPtr<JMutexAutoLock> lock(m_time_from_building.getLock()); m_time_from_building.m_value += dtime; - /* - Check the time from last addNode/removeNode. - Decrease send rate if player is building stuff. - */ if(m_time_from_building.m_value < FULL_BLOCK_SEND_ENABLE_MIN_TIME_FROM_BUILDING) { @@ -646,9 +376,11 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime, /* Send throttling - Don't allow too many simultaneous transfers + - EXCEPT when the blocks are very close Also, don't send blocks that are already flying. */ + if(d >= BLOCK_SEND_DISABLE_LIMITS_MAX_D) { JMutexAutoLock lock(m_blocks_sending_mutex); @@ -722,7 +454,7 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime, /* If block has been marked to not exist on disk (dummy) - and generating new ones is not wanted, skip block. TODO + and generating new ones is not wanted, skip block. */ if(generate == false && surely_not_found_on_disk == true) { @@ -735,16 +467,12 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime, */ if(block == NULL || surely_not_found_on_disk) { - // Block not found. - SharedPtr<JMutexAutoLock> lock - (m_num_blocks_in_emerge_queue.getLock()); + /*SharedPtr<JMutexAutoLock> lock + (m_num_blocks_in_emerge_queue.getLock());*/ //TODO: Get value from somewhere - //TODO: Balance between clients - //if(server->m_emerge_queue.size() < 1) - // Allow only one block in emerge queue - if(m_num_blocks_in_emerge_queue.m_value == 0) + if(server->m_emerge_queue.peerItemCount(peer_id) < 1) { // Add it to the emerge queue and trigger the thread @@ -752,10 +480,6 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime, if(generate == false) flags |= TOSERVER_GETBLOCK_FLAG_OPTIONAL; - { - m_num_blocks_in_emerge_queue.m_value++; - } - server->m_emerge_queue.addBlock(peer_id, p, flags); server->m_emergethread.trigger(); } @@ -880,7 +604,7 @@ void RemoteClient::SendObjectData( v3s16 center = getNodeBlockPos(center_nodepos); //s16 d_max = ACTIVE_OBJECT_D_BLOCKS; - s16 d_max = server->m_active_object_range; + s16 d_max = g_settings.getS16("active_object_range"); // Number of blocks whose objects were written to bos u16 blockcount = 0; @@ -956,9 +680,9 @@ void RemoteClient::SendObjectData( // Fetch the block only if it is on disk. // Grab and increment counter - SharedPtr<JMutexAutoLock> lock + /*SharedPtr<JMutexAutoLock> lock (m_num_blocks_in_emerge_queue.getLock()); - m_num_blocks_in_emerge_queue.m_value++; + m_num_blocks_in_emerge_queue.m_value++;*/ // Add to queue as an anonymous fetch from disk u8 flags = TOSERVER_GETBLOCK_FLAG_OPTIONAL; @@ -1072,12 +796,12 @@ void RemoteClient::SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks) } } -void RemoteClient::BlockEmerged() +/*void RemoteClient::BlockEmerged() { SharedPtr<JMutexAutoLock> lock(m_num_blocks_in_emerge_queue.getLock()); assert(m_num_blocks_in_emerge_queue.m_value > 0); m_num_blocks_in_emerge_queue.m_value--; -} +}*/ /*void RemoteClient::RunSendingTimeouts(float dtime, float timeout) { @@ -1145,19 +869,13 @@ u32 PIChecksum(core::list<PlayerInfo> &l) Server::Server( std::string mapsavedir, - bool creative_mode, HMParams hm_params, - MapParams map_params, - float objectdata_interval, - u16 active_object_range + MapParams map_params ): m_env(new ServerMap(mapsavedir, hm_params, map_params), dout_server), m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this), m_thread(this), - m_emergethread(this), - m_creative_mode(creative_mode), - m_objectdata_interval(objectdata_interval), - m_active_object_range(active_object_range) + m_emergethread(this) { m_env_mutex.Init(); m_con_mutex.Init(); @@ -1196,7 +914,7 @@ void Server::start(unsigned short port) m_thread.stop(); // Initialize connection - m_con.setTimeoutMs(50); + m_con.setTimeoutMs(30); m_con.Serve(port); // Start thread @@ -1287,7 +1005,7 @@ void Server::AsyncRunStep() // Run time- and client- related stuff // NOTE: If you intend to add something here, check that it - // doesn't fit in RemoteClient::SendBlocks for example. + // doesn't fit in RemoteClient::GetNextBlocks for example. /*{ // Clients are behind connection lock JMutexAutoLock lock(m_con_mutex); @@ -1309,7 +1027,7 @@ void Server::AsyncRunStep() { static float counter = 0.0; counter += dtime; - if(counter >= m_objectdata_interval) + if(counter >= g_settings.getFloat("objectdata_interval")) { JMutexAutoLock lock1(m_env_mutex); JMutexAutoLock lock2(m_con_mutex); @@ -1318,9 +1036,22 @@ void Server::AsyncRunStep() counter = 0.0; } } + + // Trigger emergethread (it gets somehow gets to a + // non-triggered but bysy state sometimes) + { + static float counter = 0.0; + counter += dtime; + if(counter >= 2.0) + { + counter = 0.0; + + m_emergethread.trigger(); + } + } + // Save map { - // Save map static float counter = 0.0; counter += dtime; if(counter >= SERVER_MAP_SAVE_INTERVAL) @@ -1619,7 +1350,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) // Left click if(button == 0) { - if(m_creative_mode == false) + if(g_settings.getBool("creative_mode") == false) { // Skip if inventory has no free space @@ -1684,8 +1415,6 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) { return; } - // Otherwise remove it - m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks); } catch(InvalidPositionException &e) { @@ -1707,7 +1436,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) // Send as reliable m_con.SendToAll(0, reply, true); - if(m_creative_mode == false) + if(g_settings.getBool("creative_mode") == false) { // Add to inventory and send inventory InventoryItem *item = new MaterialItem(material, 1); @@ -1715,6 +1444,12 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) SendInventory(player->peer_id); } + /* + Remove the node + (this takes some time so it is done after the quick stuff) + */ + m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks); + } // button == 0 /* Right button places blocks and stuff @@ -1744,9 +1479,6 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) MapNode n2 = m_env.getMap().getNode(p_over); if(n2.d != MATERIAL_AIR) return; - - core::map<v3s16, MapBlock*> modified_blocks; - m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks); } catch(InvalidPositionException &e) { @@ -1758,7 +1490,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) // Reset build time counter getClient(peer->id)->m_time_from_building.set(0.0); - if(m_creative_mode == false) + if(g_settings.getBool("creative_mode") == false) { // Remove from inventory and send inventory if(mitem->getCount() == 1) @@ -1779,6 +1511,14 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) n.serialize(&reply[8], peer_ser_ver); // Send as reliable m_con.SendToAll(0, reply, true); + + /* + Add node. + + This takes some time so it is done after the quick stuff + */ + core::map<v3s16, MapBlock*> modified_blocks; + m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks); } /* Handle block object items @@ -1828,7 +1568,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) //dout_server<<"Placed object"<<std::endl; - if(m_creative_mode == false) + if(g_settings.getBool("creative_mode") == false) { // Remove from inventory and send inventory player->inventory.deleteItem(item_i); @@ -2168,7 +1908,7 @@ void Server::peerAdded(con::Peer *peer) Add stuff to inventory */ - if(m_creative_mode) + if(g_settings.getBool("creative_mode")) { // Give all materials assert(USEFUL_MATERIAL_COUNT <= PLAYER_INVENTORY_SIZE); @@ -2327,35 +2067,6 @@ void Server::SendInventory(u16 peer_id) m_con.Send(peer_id, 0, data, true); } -#if 0 -void Server::SendBlocks(float dtime) -{ - DSTACK(__FUNCTION_NAME); - //dstream<<"Server::SendBlocks(): BEGIN"<<std::endl; - - JMutexAutoLock envlock(m_env_mutex); - JMutexAutoLock conlock(m_con_mutex); - - for(core::map<u16, RemoteClient*>::Iterator - i = m_clients.getIterator(); - i.atEnd() == false; i++) - { - RemoteClient *client = i.getNode()->getValue(); - assert(client->peer_id == i.getNode()->getKey()); - - if(client->serialization_version == SER_FMT_VER_INVALID) - continue; - - //dstream<<"Server::SendBlocks(): sending blocks for client "<<client->peer_id<<std::endl; - - //u16 peer_id = client->peer_id; - client->SendBlocks(this, dtime); - } - - //dstream<<"Server::SendBlocks(): END"<<std::endl; -} -#endif - void Server::SendBlocks(float dtime) { DSTACK(__FUNCTION_NAME); @@ -2390,8 +2101,9 @@ void Server::SendBlocks(float dtime) for(u32 i=0; i<queue.size(); i++) { - //TODO: Calculate value dynamically - if(total_sending >= MAX_SIMULTANEOUS_BLOCK_SENDS_SERVER_TOTAL) + //TODO: Calculate limit dynamically + if(total_sending >= g_settings.getS32 + ("max_simultaneous_block_sends_server_total")) break; PrioritySortedBlockTransfer q = queue[i]; diff --git a/src/server.h b/src/server.h index 8e2e05626..79cdf052d 100644 --- a/src/server.h +++ b/src/server.h @@ -102,6 +102,23 @@ public: JMutexAutoLock lock(m_mutex); return m_queue.size(); } + + u32 peerItemCount(u16 peer_id) + { + JMutexAutoLock lock(m_mutex); + + u32 count = 0; + + core::list<QueuedBlockEmerge*>::Iterator i; + for(i=m_queue.begin(); i!=m_queue.end(); i++) + { + QueuedBlockEmerge *q = *i; + if(q->peer_ids.find(peer_id) != NULL) + count++; + } + + return count; + } private: core::list<QueuedBlockEmerge*> m_queue; @@ -237,8 +254,8 @@ public: u8 pending_serialization_version; RemoteClient(): - m_time_from_building(0.0), - m_num_blocks_in_emerge_queue(0) + m_time_from_building(0.0) + //m_num_blocks_in_emerge_queue(0) { peer_id = 0; serialization_version = SER_FMT_VER_INVALID; @@ -276,7 +293,7 @@ public: void SetBlockNotSent(v3s16 p); void SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks); - void BlockEmerged(); + //void BlockEmerged(); /*bool IsSendingBlock(v3s16 p) { @@ -300,8 +317,8 @@ public: JMutexAutoLock l2(m_blocks_sent_mutex); JMutexAutoLock l3(m_blocks_sending_mutex); o<<"RemoteClient "<<peer_id<<": " - <<"m_num_blocks_in_emerge_queue=" - <<m_num_blocks_in_emerge_queue.get() + /*<<"m_num_blocks_in_emerge_queue=" + <<m_num_blocks_in_emerge_queue.get()*/ <<", m_blocks_sent.size()="<<m_blocks_sent.size() <<", m_blocks_sending.size()="<<m_blocks_sending.size() <<", m_nearest_unsent_d="<<m_nearest_unsent_d @@ -321,10 +338,11 @@ private: */ //TODO: core::map<v3s16, MapBlock*> m_active_blocks + //NOTE: Not here, it should be server-wide! // Number of blocks in the emerge queue that have this client as // a receiver. Used for throttling network usage. - MutexedVariable<s16> m_num_blocks_in_emerge_queue; + //MutexedVariable<s16> m_num_blocks_in_emerge_queue; /* Blocks that have been sent to client. @@ -367,17 +385,17 @@ public: NOTE: Every public method should be thread-safe */ Server( - std::string mapsavedir, - bool creative_mode, - HMParams hm_params, - MapParams map_params, - float objectdata_inverval, - u16 active_object_range - ); + std::string mapsavedir, + HMParams hm_params, + MapParams map_params + ); ~Server(); void start(unsigned short port); void stop(); + // This is mainly a way to pass the time to the server. + // Actual processing is done in an another thread. void step(float dtime); + // This is run by ServerThread and does the actual processing void AsyncRunStep(); void Receive(); void ProcessData(u8 *data, u32 datasize, u16 peer_id); @@ -387,7 +405,6 @@ public: // Environment and Connection must be locked when called void SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver); - //void SendBlock(u16 peer_id, MapBlock *block, u8 ver); //TODO: Sending of many blocks in a single packet // Environment and Connection must be locked when called @@ -431,11 +448,6 @@ private: BlockEmergeQueue m_emerge_queue; - // Settings - bool m_creative_mode; - float m_objectdata_interval; - u16 m_active_object_range; - friend class EmergeThread; friend class RemoteClient; }; diff --git a/src/socket.cpp b/src/socket.cpp index 91b2ef73a..88ba78c6a 100644 --- a/src/socket.cpp +++ b/src/socket.cpp @@ -164,7 +164,9 @@ void UDPSocket::Bind(unsigned short port) if(bind(m_handle, (const sockaddr*)&address, sizeof(sockaddr_in)) < 0) { +#ifndef DISABLE_ERRNO dstream<<(int)m_handle<<": Bind failed: "<<strerror(errno)<<std::endl; +#endif throw SocketException("Failed to bind socket"); } } @@ -291,7 +293,9 @@ bool UDPSocket::WaitData(int timeout_ms) } else if(result < 0){ // Error +#ifndef DISABLE_ERRNO dstream<<(int)m_handle<<": Select failed: "<<strerror(errno)<<std::endl; +#endif #ifdef _WIN32 dstream<<(int)m_handle<<": WSAGetLastError()="<<WSAGetLastError()<<std::endl; #endif diff --git a/src/test.cpp b/src/test.cpp index 8cdd84426..ce00306a6 100644 --- a/src/test.cpp +++ b/src/test.cpp @@ -1,6 +1,5 @@ #include "test.h" #include "common_irrlicht.h" - #include "debug.h" #include "map.h" #include "player.h" @@ -10,6 +9,7 @@ #include "connection.h" #include "utility.h" #include "serialization.h" +#include "voxel.h" #include <sstream> #ifdef _WIN32 @@ -125,6 +125,45 @@ struct TestMapNode } }; +struct TestVoxelManipulator +{ + void Run() + { + VoxelArea a(v3s16(-1,-1,-1), v3s16(1,1,1)); + assert(a.index(0,0,0) == 1*3*3 + 1*3 + 1); + assert(a.index(-1,-1,-1) == 0); + + VoxelManipulator v; + + v.print(dstream); + + dstream<<"*** Setting (-1,0,-1)=2 ***"<<std::endl; + + //v[v3s16(-1,0,-1)] = MapNode(2); + v[v3s16(-1,0,-1)].d = 2; + + v.print(dstream); + + assert(v[v3s16(-1,0,-1)].d == 2); + + dstream<<"*** Reading from inexistent (0,0,-1) ***"<<std::endl; + + assert(v[v3s16(0,0,-1)].d == MATERIAL_IGNORE); + + v.print(dstream); + + dstream<<"*** Adding area ***"<<std::endl; + + v.addArea(a); + + v.print(dstream); + + assert(v[v3s16(-1,0,-1)].d == 2); + assert(v[v3s16(0,1,1)].d == MATERIAL_IGNORE); + + } +}; + struct TestMapBlock { class TC : public NodeContainer @@ -906,6 +945,7 @@ void run_tests() TEST(TestUtilities); TEST(TestCompress); TEST(TestMapNode); + TEST(TestVoxelManipulator); TEST(TestMapBlock); TEST(TestMapSector); TEST(TestHeightmap); diff --git a/src/utility.h b/src/utility.h index e6a09547b..dabcce87c 100644 --- a/src/utility.h +++ b/src/utility.h @@ -8,8 +8,11 @@ #include "common_irrlicht.h" #include "debug.h" #include "strfnd.h" +#include "exceptions.h" #include <iostream> +#include <fstream> #include <string> +#include <sstream> extern const v3s16 g_26dirs[26]; @@ -613,5 +616,157 @@ inline s32 stoi(std::string s, s32 min, s32 max) return i; } +inline s32 stoi(std::string s) +{ + return atoi(s.c_str()); +} + +/* + Config stuff +*/ + +class Settings +{ +public: + + // Returns false on EOF + bool parseConfigObject(std::istream &is) + { + if(is.eof()) + return false; + + // NOTE: This function will be expanded to allow multi-line settings + std::string line; + std::getline(is, line); + //dstream<<"got line: \""<<line<<"\""<<std::endl; + + std::string trimmedline = trim(line); + + // Ignore comments + if(trimmedline[0] == '#') + return true; + + //dstream<<"trimmedline=\""<<trimmedline<<"\""<<std::endl; + + Strfnd sf(trim(line)); + + std::string name = sf.next("="); + name = trim(name); + + if(name == "") + return true; + + std::string value = sf.next("\n"); + value = trim(value); + + dstream<<"Config name=\""<<name<<"\" value=\"" + <<value<<"\""<<std::endl; + + m_settings[name] = value; + + return true; + } + + // Returns true on success + bool readConfigFile(const char *filename) + { + std::ifstream is(filename); + if(is.good() == false) + { + dstream<<"Error opening configuration file: " + <<filename<<std::endl; + return false; + } + + dstream<<"Parsing configuration file: " + <<filename<<std::endl; + + while(parseConfigObject(is)); + + return true; + } + + void set(std::string name, std::string value) + { + m_settings[name] = value; + } + + std::string get(std::string name) + { + core::map<std::string, std::string>::Node *n; + n = m_settings.find(name); + if(n == NULL) + throw SettingNotFoundException("Setting not found"); + + return n->getValue(); + } + + bool getBool(std::string name) + { + return is_yes(get(name)); + } + + // Asks if empty + bool getBoolAsk(std::string name, std::string question, bool def) + { + std::string s = get(name); + if(s != "") + return is_yes(s); + + char templine[10]; + std::cout<<question<<" [y/N]: "; + std::cin.getline(templine, 10); + s = templine; + + if(s == "") + return def; + + return is_yes(s); + } + + float getFloat(std::string name) + { + float f; + std::istringstream vis(get(name)); + vis>>f; + return f; + } + + u16 getU16(std::string name) + { + return stoi(get(name), 0, 65535); + } + + u16 getU16Ask(std::string name, std::string question, u16 def) + { + std::string s = get(name); + if(s != "") + return stoi(s, 0, 65535); + + char templine[10]; + std::cout<<question<<" ["<<def<<"]: "; + std::cin.getline(templine, 10); + s = templine; + + if(s == "") + return def; + + return stoi(s, 0, 65535); + } + + s16 getS16(std::string name) + { + return stoi(get(name), -32768, 32767); + } + + s32 getS32(std::string name) + { + return stoi(get(name)); + } + +private: + core::map<std::string, std::string> m_settings; +}; + #endif |