sitä sun tätä tekeillä, toimii kivasti

This commit is contained in:
Perttu Ahola 2010-11-29 10:52:07 +02:00
parent e8fd5eb8ee
commit c707e00195
19 changed files with 998 additions and 761 deletions

@ -2,7 +2,7 @@
# It's usually sufficient to change just the target name and source file list
# and be sure that CXX is set to a valid compiler
TARGET = test
SOURCE_FILES = mapblockobject.cpp inventory.cpp debug.cpp serialization.cpp light.cpp filesys.cpp connection.cpp environment.cpp client.cpp server.cpp socket.cpp mapblock.cpp mapsector.cpp heightmap.cpp map.cpp player.cpp utility.cpp main.cpp test.cpp
SOURCE_FILES = voxel.cpp mapblockobject.cpp inventory.cpp debug.cpp serialization.cpp light.cpp filesys.cpp connection.cpp environment.cpp client.cpp server.cpp socket.cpp mapblock.cpp mapsector.cpp heightmap.cpp map.cpp player.cpp utility.cpp main.cpp test.cpp
SOURCES = $(addprefix src/, $(SOURCE_FILES))
OBJECTS = $(SOURCES:.cpp=.o)
FASTTARGET = fasttest
@ -13,7 +13,7 @@ JTHREADPATH = ../jthread/jthread-1.2.1
CPPFLAGS = -I$(IRRLICHTPATH)/include -I/usr/X11R6/include -I$(JTHREADPATH)/src
#CXXFLAGS = -O2 -ffast-math -Wall -fomit-frame-pointer -pipe
CXXFLAGS = -O2 -ffast-math -Wall -g
CXXFLAGS = -O2 -ffast-math -Wall -g -pipe
#CXXFLAGS = -O1 -ffast-math -Wall -g
#CXXFLAGS = -Wall -g -O0
@ -21,8 +21,8 @@ CXXFLAGS = -O2 -ffast-math -Wall -g
#CXXFLAGS = -O3 -ffast-math -Wall -g
#CXXFLAGS = -O2 -ffast-math -Wall -g
#FASTCXXFLAGS = -O3 -ffast-math -Wall -fomit-frame-pointer -pipe -funroll-loops -mtune=pentium3
FASTCXXFLAGS = -O3 -ffast-math -Wall -fomit-frame-pointer -pipe -funroll-loops -mtune=i686
#FASTCXXFLAGS = -O3 -ffast-math -Wall -fomit-frame-pointer -pipe -funroll-loops -mtune=i686
FASTCXXFLAGS = -O3 -ffast-math -Wall -fomit-frame-pointer -pipe -funroll-loops -mtune=i686 -fwhole-program
#Default target
@ -53,7 +53,9 @@ all_linux all_win32: $(DESTPATH)
fast_linux: $(FASTDESTPATH)
$(FASTDESTPATH): $(SOURCES)
$(CXX) -o $(FASTDESTPATH) $(SOURCES) $(CPPFLAGS) $(FASTCXXFLAGS) $(LDFLAGS) -DUNITTEST_DISABLE
@#$(CXX) -o $(FASTDESTPATH) $(SOURCES) $(CPPFLAGS) $(FASTCXXFLAGS) $(LDFLAGS) -DUNITTEST_DISABLE
@# Errno doesn't work ("error: __errno_location was not declared in this scope")
cat $(SOURCES) | $(CXX) -o $(FASTDESTPATH) -x c++ - -Isrc/ $(CPPFLAGS) $(FASTCXXFLAGS) $(LDFLAGS) -DUNITTEST_DISABLE -DDISABLE_ERRNO
$(DESTPATH): $(OBJECTS)
$(CXX) -o $@ $(OBJECTS) $(LDFLAGS)

BIN
data/mud.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 831 B

@ -32,6 +32,7 @@ cp -r data/light.png $PACKAGEPATH/data/
cp -r data/sign.png $PACKAGEPATH/data/
cp -r data/sign_back.png $PACKAGEPATH/data/
cp -r data/rat.png $PACKAGEPATH/data/
cp -r data/mud.png $PACKAGEPATH/data/
cp -r doc/README.txt $PACKAGEPATH/doc/README.txt

@ -44,4 +44,8 @@ creative_mode = false
# Player and object positions are sent at intervals specified by this
objectdata_inverval = 0.2
active_object_range = 2
max_simultaneous_block_sends_per_client = 2
max_simultaneous_block_sends_server_total = 4

@ -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,6 +1213,18 @@ bool Client::AsyncProcessPacket(LazyMeshUpdater &mesh_updater)
bool Client::AsyncProcessData()
{
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(;;)
{
@ -1214,7 +1232,7 @@ bool Client::AsyncProcessData()
if(r == false)
break;
}
return false;
return false;*/
}
void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)

@ -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
//#define MAX_SIMULTANEOUS_BLOCK_SENDS_SERVER_TOTAL 4
// Viewing range stuff
#define FPS_DEFAULT_WANTED 30
#define FPS_DEFAULT_MAX 60
#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

@ -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:
*/

@ -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;
Settings g_settings;
// 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;
// Sets default settings
void set_default_settings()
{
g_settings.set("dedicated_server", "");
// 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;
// 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
@ -876,8 +760,10 @@ void updateViewingRange(f32 frametime, Client *client)
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);
}
// Dedicated?
bool dedicated = g_settings.getBoolAsk
("dedicated_server", "Dedicated server?", false);
std::cout<<"dedicated = "<<dedicated<<std::endl;
bool dedicated = false;
if(templine[0] == 'y')
dedicated = true;
if(dedicated)
std::cout<<"-> yes"<<std::endl;
else
std::cout<<"-> no"<<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)

@ -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

@ -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,
#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
@ -193,6 +213,15 @@ void Map::unLightNeighbors(v3s16 pos, u8 oldlight,
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];
// Get the block where the node is located
v3s16 blockpos = getNodeBlockPos(n2pos);
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);
// 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)
@ -228,32 +256,74 @@ void Map::unLightNeighbors(v3s16 pos, u8 oldlight,
if(block->isDummy())
continue;
// Calculate relative position in block
v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
// Get node straight from the block
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;
/*
If the neighbor is dimmer than what was specified
as oldlight (the light of the previous node)
If the neighbor is brighter than the current node,
add to list (it will light up this node on its turn)
*/
if(n2.getLight() < oldlight)
if(n2.getLight() > undiminish_light(oldlight))
{
lighted_nodes.insert(n2pos, true);
//lighted_nodes.push_back(n2pos);
changed = true;
}
/*
And the neighbor is transparent and it has some light
If the neighbor is dimmer than how much light this node
would spread on it, add to list
*/
if(n2.light_propagates() && n2.getLight() != 0)
if(n2.getLight() < newlight)
{
/*
Set light to 0 and recurse.
*/
u8 current_light = n2.getLight();
n2.setLight(0);
if(n2.light_propagates())
{
n2.setLight(newlight);
block->setNode(relpos, n2);
unLightNeighbors(n2pos, current_light,
light_sources, modified_blocks);
lighted_nodes.insert(n2pos, true);
//lighted_nodes.push_back(n2pos);
changed = true;
}
}
if(block_checked_in_modified == false)
// 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)
{
// 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
@ -1628,11 +1750,21 @@ MapBlock * ServerMap::emergeBlock(
bool is_underground = (block_y+1) * MAP_BLOCKSIZE < lowest_ground_y;
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.
*/
/*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);
}*/
/*
{
v3s16 pos(8, 11, 8);
SignObject *obj = new SignObject(NULL, -1, intToFloat(pos));
obj->setText("Moicka");
obj->setYaw(45);
block->addObject(obj);
}
{
v3s16 pos(8, 11, 8);
RatObject *obj = new RatObject(NULL, -1, intToFloat(pos));
block->addObject(obj);
}
*/
/*
Add block to sector.
*/
sector->insertBlock(block);
// An y-wise container if changed blocks
/*
Do some interpolation for dungeons
*/
#if 0
{
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++)
{
MapBlock *block = i.getNode()->getValue();
changed_blocks.insert(block->getPos(), block);
//lighting_invalidated_blocks.insert(block->getPos(), block);
}
}
#endif
/*
Sector object stuff
*/
// 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.

152
src/map.h

@ -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:
InvalidFilenameException(const char *s):
BaseException(s)
{}
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:
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
@ -61,6 +178,13 @@ 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

@ -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
@ -245,6 +247,35 @@ public:
setNode(p.X, p.Y, p.Z, n);
}
/*
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.

@ -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
/*
@ -64,6 +79,8 @@ enum Material
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);

@ -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

@ -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);
@ -1319,8 +1037,21 @@ void Server::AsyncRunStep()
}
}
// 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
{
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];

@ -103,6 +103,23 @@ public:
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;
JMutex m_mutex;
@ -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.
@ -368,16 +386,16 @@ public:
*/
Server(
std::string mapsavedir,
bool creative_mode,
HMParams hm_params,
MapParams map_params,
float objectdata_inverval,
u16 active_object_range
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;
};

@ -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

@ -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);

@ -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