Merge branch 'upstream/master'

Conflicts:
	data/oerkki1.png
	src/client.cpp
This commit is contained in:
Nils Dagsson Moskopp 2011-07-14 22:43:22 +02:00
commit 3560f0de08
53 changed files with 3330 additions and 2841 deletions

@ -9,7 +9,12 @@ src/jthread/CMakeFiles/*
src/jthread/Makefile src/jthread/Makefile
src/jthread/cmake_config.h src/jthread/cmake_config.h
src/jthread/cmake_install.cmake src/jthread/cmake_install.cmake
src/.*.swp
src/sqlite/libsqlite3.a
src/session.vim
util/uloste.png
minetest.conf minetest.conf
debug.txt
bin/ bin/
CMakeCache.txt CMakeCache.txt
CPackConfig.cmake CPackConfig.cmake

@ -9,7 +9,7 @@ project(minetest)
set(VERSION_MAJOR 0) set(VERSION_MAJOR 0)
set(VERSION_MINOR 2) set(VERSION_MINOR 2)
set(VERSION_PATCH 20110618_0_dev) set(VERSION_PATCH 20110704_0)
set(VERSION_STRING "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}") set(VERSION_STRING "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}")
# Configuration options # Configuration options

BIN
data/oerkki1_damaged.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 341 B

BIN
data/unknown_block.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 582 B

@ -3,6 +3,11 @@ Minetest-c55 changelog
This should contain all the major changes. This should contain all the major changes.
For minor stuff, refer to the commit log of the repository. For minor stuff, refer to the commit log of the repository.
2011-07-04:
- Many small fixes
- Code reorganizing to aid further development
- Renewed map generator
2011-06-02: 2011-06-02:
- Password crash on windows fixed - Password crash on windows fixed
- Optimized server CPU usage a lot - Optimized server CPU usage a lot

@ -9,25 +9,28 @@
# #
# Further documentation: # Further documentation:
# http://celeron.55.lt/~celeron55/minetest/wiki/doku.php # http://celeron.55.lt/~celeron55/minetest/wiki/doku.php
#
# NOTE: This file might not be up-to-date, refer to the
# defaultsettings.cpp file for an up-to-date list:
# https://bitbucket.org/celeron55/minetest/src/tip/src/defaultsettings.cpp
#
# A vim command to convert most of defaultsettings.cpp to conf file format:
# :'<,'>s/\tg_settings\.setDefault("\([^"]*\)", "\([^"]*\)");.*/#\1 = \2/g
# #
# Client side stuff # Client and server
# #
# Initial window size # Network port (UDP)
#screenW = 800 #port =
#screenH = 600 # Name of player; on a server this is the main admin
# Port to connect to and to bind a server at
#port = 30000
# Address to connect to (blank = start local server)
#address =
# Name of player. On server, this is the default admin.
#name = #name =
# Key configuration. #
# Client stuff
#
# Key mappings
# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 # See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3
#keymap_forward = KEY_KEY_W #keymap_forward = KEY_KEY_W
#keymap_backward = KEY_KEY_S #keymap_backward = KEY_KEY_S
@ -38,108 +41,99 @@
#keymap_inventory = KEY_KEY_I #keymap_inventory = KEY_KEY_I
#keymap_chat = KEY_KEY_T #keymap_chat = KEY_KEY_T
#keymap_rangeselect = KEY_KEY_R #keymap_rangeselect = KEY_KEY_R
# Some (temporary) keys for debugging features #keymap_freemove = KEY_KEY_K
#keymap_fastmove = KEY_KEY_J
#keymap_frametime_graph = KEY_F1
#keymap_screenshot = KEY_F12
# Some (temporary) keys for debugging
#keymap_special1 = KEY_KEY_E #keymap_special1 = KEY_KEY_E
#keymap_print_debug_stacks = KEY_KEY_P #keymap_print_debug_stacks = KEY_KEY_P
#invert_mouse = false
# The desired FPS # The desired FPS
#wanted_fps = 30 #wanted_fps = 30
# If FPS would go higher than this, limit it by sleeping # If FPS would go higher than this, limit it by sleeping
# (to not waste CPU power for no benefit) # (to not waste CPU power for no benefit)
#fps_max = 60 #fps_max = 60
# The allowed adjustment range for the automatic rendering range adjustment # The allowed adjustment range for the automatic rendering range adjustment
#viewing_range_nodes_max = 300 #viewing_range_nodes_max = 300
#viewing_range_nodes_min = 35 #viewing_range_nodes_min = 25
# Initial window size
screenW# = 800
screenH# = 600
# Address to connect to (#blank = start local server)
#address =
# Enable random user input, for testing
#random_input = false
# Timeout for client to remove unused map data from memory
#client_unload_unused_data_timeout = 600
# Whether to fog out the end of the visible area # Whether to fog out the end of the visible area
#enable_fog = true #enable_fog = true
# Enable a bit lower water surface; disable for speed (not quite optimized)
# Enable/disable clouds #new_style_water = false
#enable_clouds = true
# Experimental
#enable_farmesh = false
# Enable a bit lower water surface; disable for speed
#new_style_water = true
# Enable nice leaves; disable for speed # Enable nice leaves; disable for speed
#new_style_leaves = true #new_style_leaves = true
# Enable smooth lighting with simple ambient occlusion; # Enable smooth lighting with simple ambient occlusion;
# disable for speed or for different looks. # disable for speed or for different looks.
#smooth_lighting = true #smooth_lighting = true
# Whether to draw a frametime graph (for debugging frametime) # Whether to draw a frametime graph (for debugging frametime)
#frametime_graph = false #frametime_graph = false
# Enable combining mainly used textures to a bigger one for improved speed # Enable combining mainly used textures to a bigger one for improved speed
# disable if it causes graphics glitches. # disable if it causes graphics glitches.
#enable_texture_atlas = true #enable_texture_atlas = true
# Path to texture directory. All textures are first searched from here. # Path to texture directory. All textures are first searched from here.
#texture_path = #texture_path =
# Video back-end. # Video back-end.
# Possible values: null, software, burningsvideo, direct3d8, direct3d9, opengl # Possible values: null, software, burningsvideo, direct3d8, direct3d9, opengl
#video_driver = opengl #video_driver = opengl
# Unobstructed movement without physics, downwards key is keymap_special1
# Enable random user input, for testing #free_move = false
#random_input = false # Continuous forward movement (for testing)
#continuous_forward = false
# Timeout for client to remove unused map data from memory # Fast movement (keymap_special1)
#client_delete_unused_sectors_timeout = 1200 #fast_move = false
# Invert mouse
#invert_mouse = false
# FarMesh thingy
#enable_farmesh = false
# Enable/disable clouds
#enable_clouds = true
# Don't draw stone (for testing)
#invisible_stone = false
# Path for screenshots
#screenshot_path = .
# #
# Server side stuff # Server stuff
# #
# Map directory (everything in the world is stored here) # Map directory (everything in the world is stored here)
#map-dir = /home/palle/custom_map #map-#dir = /custom/map
# Set to true to enable experimental features or stuff that is tested
# (varies from version to version, usually not useful at all)
#enable_experimental = false
# Set to true to enable creative mode (unlimited inventory) # Set to true to enable creative mode (unlimited inventory)
#creative_mode = false #creative_mode = false
#enable_damage = false #enable_damage = false
#default_password =
# Available privileges: build, teleport, settime, privs, shout
#default_privs = build, shout
# Gives some stuff to players at the beginning # Gives some stuff to players at the beginning
#give_initial_stuff = false #give_initial_stuff = false
#default_password =
# Set to true to enable experimental features # Available privileges: build, teleport, settime, privs, shout
# (varies from version to version, see wiki) #default_privs = build, shout
#enable_experimental = false # Profiler data print interval. #0 = disable.
#profiler_print_interval = 0
# Profiler data print interval. 0 = disable. #enable_mapgen_debug_info = false
#profiler_print_interval = 10
# Player and object positions are sent at intervals specified by this # Player and object positions are sent at intervals specified by this
#objectdata_inverval = 0.2 #objectdata_interval = 0.2
#active_object_range = 2 #active_object_range = 2
#max_simultaneous_block_sends_per_client = 2
#max_simultaneous_block_sends_per_client = 1 #max_simultaneous_block_sends_server_total = 8
#max_simultaneous_block_sends_server_total = 4 #max_block_send_distance = 8
#max_block_generate_distance = 8
#max_block_send_distance = 5 #time_send_interval = 20
#max_block_generate_distance = 4 # Length of day/night cycle. 72=20min, 360=4min, 1=24hour
# 20 min/day
#time_speed = 72 #time_speed = 72
# 4 min/day #server_unload_unused_data_timeout = 60
#time_speed = 360
# 1 min/day
#time_speed = 1440
#time_send_interval = 5
#server_unload_unused_sectors_timeout = 60
#server_map_save_interval = 60 #server_map_save_interval = 60
#full_block_send_enable_min_time_from_building = 2.0

@ -61,6 +61,7 @@ configure_file(
) )
set(common_SRCS set(common_SRCS
content_sao.cpp
mapgen.cpp mapgen.cpp
content_inventory.cpp content_inventory.cpp
content_nodemeta.cpp content_nodemeta.cpp
@ -102,6 +103,7 @@ set(common_SRCS
# Client sources # Client sources
set(minetest_SRCS set(minetest_SRCS
${common_SRCS} ${common_SRCS}
content_cao.cpp
mapblock_mesh.cpp mapblock_mesh.cpp
farmesh.cpp farmesh.cpp
keycode.cpp keycode.cpp

@ -23,6 +23,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "common_irrlicht.h" #include "common_irrlicht.h"
#include <string> #include <string>
#define ACTIVEOBJECT_TYPE_INVALID 0
// Other types are defined in content_object.h
struct ActiveObjectMessage struct ActiveObjectMessage
{ {
ActiveObjectMessage(u16 id_, bool reliable_=true, std::string data_=""): ActiveObjectMessage(u16 id_, bool reliable_=true, std::string data_=""):
@ -36,12 +39,6 @@ struct ActiveObjectMessage
std::string datastring; std::string datastring;
}; };
#define ACTIVEOBJECT_TYPE_INVALID 0
#define ACTIVEOBJECT_TYPE_TEST 1
#define ACTIVEOBJECT_TYPE_ITEM 2
#define ACTIVEOBJECT_TYPE_RAT 3
#define ACTIVEOBJECT_TYPE_OERKKI1 4
/* /*
Parent class for ServerActiveObject and ClientActiveObject Parent class for ServerActiveObject and ClientActiveObject
*/ */

@ -25,6 +25,105 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "main.h" #include "main.h"
#include <sstream> #include <sstream>
#include "porting.h" #include "porting.h"
#include "mapsector.h"
#include "mapblock_mesh.h"
#include "mapblock.h"
/*
QueuedMeshUpdate
*/
QueuedMeshUpdate::QueuedMeshUpdate():
p(-1337,-1337,-1337),
data(NULL),
ack_block_to_server(false)
{
}
QueuedMeshUpdate::~QueuedMeshUpdate()
{
if(data)
delete data;
}
/*
MeshUpdateQueue
*/
MeshUpdateQueue::MeshUpdateQueue()
{
m_mutex.Init();
}
MeshUpdateQueue::~MeshUpdateQueue()
{
JMutexAutoLock lock(m_mutex);
core::list<QueuedMeshUpdate*>::Iterator i;
for(i=m_queue.begin(); i!=m_queue.end(); i++)
{
QueuedMeshUpdate *q = *i;
delete q;
}
}
/*
peer_id=0 adds with nobody to send to
*/
void MeshUpdateQueue::addBlock(v3s16 p, MeshMakeData *data, bool ack_block_to_server)
{
DSTACK(__FUNCTION_NAME);
assert(data);
JMutexAutoLock lock(m_mutex);
/*
Find if block is already in queue.
If it is, update the data and quit.
*/
core::list<QueuedMeshUpdate*>::Iterator i;
for(i=m_queue.begin(); i!=m_queue.end(); i++)
{
QueuedMeshUpdate *q = *i;
if(q->p == p)
{
if(q->data)
delete q->data;
q->data = data;
if(ack_block_to_server)
q->ack_block_to_server = true;
return;
}
}
/*
Add the block
*/
QueuedMeshUpdate *q = new QueuedMeshUpdate;
q->p = p;
q->data = data;
q->ack_block_to_server = ack_block_to_server;
m_queue.push_back(q);
}
// Returned pointer must be deleted
// Returns NULL if queue is empty
QueuedMeshUpdate * MeshUpdateQueue::pop()
{
JMutexAutoLock lock(m_mutex);
core::list<QueuedMeshUpdate*>::Iterator i = m_queue.begin();
if(i == m_queue.end())
return NULL;
QueuedMeshUpdate *q = *i;
m_queue.erase(i);
return q;
}
/*
MeshUpdateThread
*/
void * MeshUpdateThread::Thread() void * MeshUpdateThread::Thread()
{ {
@ -36,6 +135,15 @@ void * MeshUpdateThread::Thread()
while(getRun()) while(getRun())
{ {
/*// Wait for output queue to flush.
// Allow 2 in queue, this makes less frametime jitter.
// Umm actually, there is no much difference
if(m_queue_out.size() >= 2)
{
sleep_ms(3);
continue;
}*/
QueuedMeshUpdate *q = m_queue_in.pop(); QueuedMeshUpdate *q = m_queue_in.pop();
if(q == NULL) if(q == NULL)
{ {
@ -91,7 +199,7 @@ Client::Client(
m_access_denied(false) m_access_denied(false)
{ {
m_packetcounter_timer = 0.0; m_packetcounter_timer = 0.0;
m_delete_unused_sectors_timer = 0.0; //m_delete_unused_sectors_timer = 0.0;
m_connection_reinit_timer = 0.0; m_connection_reinit_timer = 0.0;
m_avg_rtt_timer = 0.0; m_avg_rtt_timer = 0.0;
m_playerpos_send_timer = 0.0; m_playerpos_send_timer = 0.0;
@ -195,7 +303,11 @@ void Client::step(float dtime)
m_packetcounter.clear(); m_packetcounter.clear();
} }
} }
// Get connection status
bool connected = connectedAndInitialized();
#if 0
{ {
/* /*
Delete unused sectors Delete unused sectors
@ -225,16 +337,16 @@ void Client::step(float dtime)
true, &deleted_blocks);*/ true, &deleted_blocks);*/
// Delete whole sectors // Delete whole sectors
u32 num = m_env.getMap().unloadUnusedData m_env.getMap().unloadUnusedData
(delete_unused_sectors_timeout, (delete_unused_sectors_timeout,
false, &deleted_blocks); &deleted_blocks);
if(num > 0) if(deleted_blocks.size() > 0)
{ {
/*dstream<<DTIME<<"Client: Deleted blocks of "<<num /*dstream<<DTIME<<"Client: Deleted blocks of "<<num
<<" unused sectors"<<std::endl;*/ <<" unused sectors"<<std::endl;*/
dstream<<DTIME<<"Client: Deleted "<<num /*dstream<<DTIME<<"Client: Deleted "<<num
<<" unused sectors"<<std::endl; <<" unused sectors"<<std::endl;*/
/* /*
Send info to server Send info to server
@ -284,8 +396,7 @@ void Client::step(float dtime)
} }
} }
} }
#endif
bool connected = connectedAndInitialized();
if(connected == false) if(connected == false)
{ {
@ -330,6 +441,67 @@ void Client::step(float dtime)
Do stuff if connected Do stuff if connected
*/ */
/*
Run Map's timers and unload unused data
*/
const float map_timer_and_unload_dtime = 5.25;
if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
{
ScopeProfiler sp(&g_profiler, "Client: map timer and unload");
core::list<v3s16> deleted_blocks;
m_env.getMap().timerUpdate(map_timer_and_unload_dtime,
g_settings.getFloat("client_unload_unused_data_timeout"),
&deleted_blocks);
/*if(deleted_blocks.size() > 0)
dstream<<"Client: Unloaded "<<deleted_blocks.size()
<<" unused blocks"<<std::endl;*/
/*
Send info to server
NOTE: This loop is intentionally iterated the way it is.
*/
core::list<v3s16>::Iterator i = deleted_blocks.begin();
core::list<v3s16> sendlist;
for(;;)
{
if(sendlist.size() == 255 || i == deleted_blocks.end())
{
if(sendlist.size() == 0)
break;
/*
[0] u16 command
[2] u8 count
[3] v3s16 pos_0
[3+6] v3s16 pos_1
...
*/
u32 replysize = 2+1+6*sendlist.size();
SharedBuffer<u8> reply(replysize);
writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
reply[2] = sendlist.size();
u32 k = 0;
for(core::list<v3s16>::Iterator
j = sendlist.begin();
j != sendlist.end(); j++)
{
writeV3S16(&reply[2+1+6*k], *j);
k++;
}
m_con.Send(PEER_ID_SERVER, 1, reply, true);
if(i == deleted_blocks.end())
break;
sendlist.clear();
}
sendlist.push_back(*i);
i++;
}
}
/* /*
Handle environment Handle environment
*/ */
@ -345,23 +517,23 @@ void Client::step(float dtime)
//TimeTaker envtimer("env step", m_device); //TimeTaker envtimer("env step", m_device);
// Step environment // Step environment
m_env.step(dtime); m_env.step(dtime);
// Step active blocks /*
Handle active blocks
NOTE: These old objects are DEPRECATED. TODO: Remove
*/
for(core::map<v3s16, bool>::Iterator for(core::map<v3s16, bool>::Iterator
i = m_active_blocks.getIterator(); i = m_active_blocks.getIterator();
i.atEnd() == false; i++) i.atEnd() == false; i++)
{ {
v3s16 p = i.getNode()->getKey(); v3s16 p = i.getNode()->getKey();
MapBlock *block = NULL; MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(p);
try if(block == NULL)
{ continue;
block = m_env.getMap().getBlockNoCreate(p);
block->stepObjects(dtime, false, m_env.getDayNightRatio()); // Step MapBlockObjects
} block->stepObjects(dtime, false, m_env.getDayNightRatio());
catch(InvalidPositionException &e)
{
}
} }
/* /*
@ -695,78 +867,43 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
MapSector *sector; MapSector *sector;
MapBlock *block; MapBlock *block;
{ //envlock v2s16 p2d(p.X, p.Z);
//JMutexAutoLock envlock(m_env_mutex); //bulk comment-out sector = m_env.getMap().emergeSector(p2d);
v2s16 p2d(p.X, p.Z); assert(sector->getPos() == p2d);
sector = m_env.getMap().emergeSector(p2d);
v2s16 sp = sector->getPos();
if(sp != p2d)
{
dstream<<"ERROR: Got sector with getPos()="
<<"("<<sp.X<<","<<sp.Y<<"), tried to get"
<<"("<<p2d.X<<","<<p2d.Y<<")"<<std::endl;
}
assert(sp == p2d); //TimeTaker timer("MapBlock deSerialize");
//assert(sector->getPos() == p2d); // 0ms
block = sector->getBlockNoCreateNoEx(p.Y);
if(block)
{
/*
Update an existing block
*/
//dstream<<"Updating"<<std::endl;
block->deSerialize(istr, ser_version);
}
else
{
/*
Create a new block
*/
//dstream<<"Creating new"<<std::endl;
block = new MapBlock(&m_env.getMap(), p);
block->deSerialize(istr, ser_version);
sector->insertBlock(block);
//TimeTaker timer("MapBlock deSerialize"); //DEBUG
// 0ms /*NodeMod mod;
mod.type = NODEMOD_CHANGECONTENT;
try{ mod.param = CONTENT_MESE;
block = sector->getBlockNoCreate(p.Y); block->setTempMod(v3s16(8,10,8), mod);
/* block->setTempMod(v3s16(8,9,8), mod);
Update an existing block block->setTempMod(v3s16(8,8,8), mod);
*/ block->setTempMod(v3s16(8,7,8), mod);
//dstream<<"Updating"<<std::endl; block->setTempMod(v3s16(8,6,8), mod);*/
block->deSerialize(istr, ser_version); }
//block->setChangedFlag();
}
catch(InvalidPositionException &e)
{
/*
Create a new block
*/
//dstream<<"Creating new"<<std::endl;
block = new MapBlock(&m_env.getMap(), p);
block->deSerialize(istr, ser_version);
sector->insertBlock(block);
//block->setChangedFlag();
//DEBUG
/*NodeMod mod;
mod.type = NODEMOD_CHANGECONTENT;
mod.param = CONTENT_MESE;
block->setTempMod(v3s16(8,10,8), mod);
block->setTempMod(v3s16(8,9,8), mod);
block->setTempMod(v3s16(8,8,8), mod);
block->setTempMod(v3s16(8,7,8), mod);
block->setTempMod(v3s16(8,6,8), mod);*/
#if 0
/*
Add some coulds
Well, this is a dumb way to do it, they should just
be drawn as separate objects. But the looks of them
can be tested this way.
*/
if(p.Y == 3)
{
NodeMod mod;
mod.type = NODEMOD_CHANGECONTENT;
mod.param = CONTENT_CLOUD;
v3s16 p2;
p2.Y = 8;
for(p2.X=3; p2.X<=13; p2.X++)
for(p2.Z=3; p2.Z<=13; p2.Z++)
{
block->setTempMod(p2, mod);
}
}
#endif
}
} //envlock
#if 0 #if 0
/* /*
@ -798,6 +935,7 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
/* /*
Add it to mesh update queue and set it to be acknowledged after update. Add it to mesh update queue and set it to be acknowledged after update.
*/ */
//std::cerr<<"Adding mesh update task for received block"<<std::endl;
addUpdateMeshTaskWithEdge(p, true); addUpdateMeshTaskWithEdge(p, true);
} }
else if(command == TOCLIENT_PLAYERPOS) else if(command == TOCLIENT_PLAYERPOS)
@ -974,6 +1112,8 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
} }
else if(command == TOCLIENT_SECTORMETA) else if(command == TOCLIENT_SECTORMETA)
{ {
dstream<<"Client received DEPRECATED TOCLIENT_SECTORMETA"<<std::endl;
#if 0
/* /*
[0] u16 command [0] u16 command
[2] u8 sector count [2] u8 sector count
@ -1009,6 +1149,7 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
((ClientMap&)m_env.getMap()).deSerializeSector(pos, is); ((ClientMap&)m_env.getMap()).deSerializeSector(pos, is);
} }
} //envlock } //envlock
#endif
} }
else if(command == TOCLIENT_INVENTORY) else if(command == TOCLIENT_INVENTORY)
{ {
@ -1105,6 +1246,7 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
/* /*
Read block objects Read block objects
NOTE: Deprecated stuff here, TODO: Remove
*/ */
// Read active block count // Read active block count
@ -1753,7 +1895,7 @@ void Client::addNode(v3s16 p, MapNode n)
try try
{ {
TimeTaker timer3("Client::addNode(): addNodeAndUpdate"); //TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
m_env.getMap().addNodeAndUpdate(p, n, modified_blocks); m_env.getMap().addNodeAndUpdate(p, n, modified_blocks);
} }
catch(InvalidPositionException &e) catch(InvalidPositionException &e)
@ -1981,12 +2123,6 @@ void Client::printDebugInfo(std::ostream &os)
<<std::endl;*/ <<std::endl;*/
} }
/*s32 Client::getDayNightIndex()
{
assert(m_daynight_i >= 0 && m_daynight_i < DAYNIGHT_CACHE_COUNT);
return m_daynight_i;
}*/
u32 Client::getDayNightRatio() u32 Client::getDayNightRatio()
{ {
//JMutexAutoLock envlock(m_env_mutex); //bulk comment-out //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
@ -2000,6 +2136,40 @@ u16 Client::getHP()
return player->hp; return player->hp;
} }
void Client::setTempMod(v3s16 p, NodeMod mod)
{
//JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
assert(m_env.getMap().mapType() == MAPTYPE_CLIENT);
core::map<v3s16, MapBlock*> affected_blocks;
((ClientMap&)m_env.getMap()).setTempMod(p, mod,
&affected_blocks);
for(core::map<v3s16, MapBlock*>::Iterator
i = affected_blocks.getIterator();
i.atEnd() == false; i++)
{
i.getNode()->getValue()->updateMesh(m_env.getDayNightRatio());
}
}
void Client::clearTempMod(v3s16 p)
{
//JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
assert(m_env.getMap().mapType() == MAPTYPE_CLIENT);
core::map<v3s16, MapBlock*> affected_blocks;
((ClientMap&)m_env.getMap()).clearTempMod(p,
&affected_blocks);
for(core::map<v3s16, MapBlock*>::Iterator
i = affected_blocks.getIterator();
i.atEnd() == false; i++)
{
i.getNode()->getValue()->updateMesh(m_env.getDayNightRatio());
}
}
void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server) void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server)
{ {
/*dstream<<"Client::addUpdateMeshTask(): " /*dstream<<"Client::addUpdateMeshTask(): "
@ -2009,7 +2179,7 @@ void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server)
MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p); MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
if(b == NULL) if(b == NULL)
return; return;
/* /*
Create a task to update the mesh of the block Create a task to update the mesh of the block
*/ */
@ -2018,7 +2188,8 @@ void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server)
{ {
//TimeTaker timer("data fill"); //TimeTaker timer("data fill");
// 0ms // Release: ~0ms
// Debug: 1-6ms, avg=2ms
data->fill(getDayNightRatio(), b); data->fill(getDayNightRatio(), b);
} }
@ -2044,6 +2215,10 @@ void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server)
} }
#endif #endif
/*
Mark mesh as non-expired at this point so that it can already
be marked as expired again if the data changes
*/
b->setMeshExpired(false); b->setMeshExpired(false);
} }

@ -28,6 +28,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "jmutex.h" #include "jmutex.h"
#include <ostream> #include <ostream>
#include "clientobject.h" #include "clientobject.h"
#include "utility.h" // For IntervalLimiter
struct MeshMakeData;
class ClientNotReadyException : public BaseException class ClientNotReadyException : public BaseException
{ {
@ -43,18 +46,8 @@ struct QueuedMeshUpdate
MeshMakeData *data; MeshMakeData *data;
bool ack_block_to_server; bool ack_block_to_server;
QueuedMeshUpdate(): QueuedMeshUpdate();
p(-1337,-1337,-1337), ~QueuedMeshUpdate();
data(NULL),
ack_block_to_server(false)
{
}
~QueuedMeshUpdate()
{
if(data)
delete data;
}
}; };
/* /*
@ -63,76 +56,18 @@ struct QueuedMeshUpdate
class MeshUpdateQueue class MeshUpdateQueue
{ {
public: public:
MeshUpdateQueue() MeshUpdateQueue();
{
m_mutex.Init();
}
~MeshUpdateQueue() ~MeshUpdateQueue();
{
JMutexAutoLock lock(m_mutex);
core::list<QueuedMeshUpdate*>::Iterator i;
for(i=m_queue.begin(); i!=m_queue.end(); i++)
{
QueuedMeshUpdate *q = *i;
delete q;
}
}
/* /*
peer_id=0 adds with nobody to send to peer_id=0 adds with nobody to send to
*/ */
void addBlock(v3s16 p, MeshMakeData *data, bool ack_block_to_server) void addBlock(v3s16 p, MeshMakeData *data, bool ack_block_to_server);
{
DSTACK(__FUNCTION_NAME);
assert(data);
JMutexAutoLock lock(m_mutex);
/*
Find if block is already in queue.
If it is, update the data and quit.
*/
core::list<QueuedMeshUpdate*>::Iterator i;
for(i=m_queue.begin(); i!=m_queue.end(); i++)
{
QueuedMeshUpdate *q = *i;
if(q->p == p)
{
if(q->data)
delete q->data;
q->data = data;
if(ack_block_to_server)
q->ack_block_to_server = true;
return;
}
}
/*
Add the block
*/
QueuedMeshUpdate *q = new QueuedMeshUpdate;
q->p = p;
q->data = data;
q->ack_block_to_server = ack_block_to_server;
m_queue.push_back(q);
}
// Returned pointer must be deleted // Returned pointer must be deleted
// Returns NULL if queue is empty // Returns NULL if queue is empty
QueuedMeshUpdate * pop() QueuedMeshUpdate * pop();
{
JMutexAutoLock lock(m_mutex);
core::list<QueuedMeshUpdate*>::Iterator i = m_queue.begin();
if(i == m_queue.end())
return NULL;
QueuedMeshUpdate *q = *i;
m_queue.erase(i);
return q;
}
u32 size() u32 size()
{ {
@ -309,40 +244,8 @@ public:
u16 getHP(); u16 getHP();
//void updateSomeExpiredMeshes(); void setTempMod(v3s16 p, NodeMod mod);
void clearTempMod(v3s16 p);
void setTempMod(v3s16 p, NodeMod mod)
{
//JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
assert(m_env.getMap().mapType() == MAPTYPE_CLIENT);
core::map<v3s16, MapBlock*> affected_blocks;
((ClientMap&)m_env.getMap()).setTempMod(p, mod,
&affected_blocks);
for(core::map<v3s16, MapBlock*>::Iterator
i = affected_blocks.getIterator();
i.atEnd() == false; i++)
{
i.getNode()->getValue()->updateMesh(m_env.getDayNightRatio());
}
}
void clearTempMod(v3s16 p)
{
//JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
assert(m_env.getMap().mapType() == MAPTYPE_CLIENT);
core::map<v3s16, MapBlock*> affected_blocks;
((ClientMap&)m_env.getMap()).clearTempMod(p,
&affected_blocks);
for(core::map<v3s16, MapBlock*>::Iterator
i = affected_blocks.getIterator();
i.atEnd() == false; i++)
{
i.getNode()->getValue()->updateMesh(m_env.getDayNightRatio());
}
}
float getAvgRtt() float getAvgRtt()
{ {
@ -389,6 +292,15 @@ public:
{ {
return m_access_denied_reason; return m_access_denied_reason;
} }
/*
This should only be used for calling the special drawing stuff in
ClientEnvironment
*/
ClientEnvironment * getEnv()
{
return &m_env;
}
private: private:
@ -404,11 +316,11 @@ private:
void sendPlayerInfo(); void sendPlayerInfo();
float m_packetcounter_timer; float m_packetcounter_timer;
float m_delete_unused_sectors_timer;
float m_connection_reinit_timer; float m_connection_reinit_timer;
float m_avg_rtt_timer; float m_avg_rtt_timer;
float m_playerpos_send_timer; float m_playerpos_send_timer;
float m_ignore_damage_timer; // Used after server moves player float m_ignore_damage_timer; // Used after server moves player
IntervalLimiter m_map_timer_and_unload_interval;
MeshUpdateThread m_mesh_update_thread; MeshUpdateThread m_mesh_update_thread;

@ -21,9 +21,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "debug.h" #include "debug.h"
#include "porting.h" #include "porting.h"
#include "constants.h" #include "constants.h"
#include "utility.h"
#include "environment.h"
#include "tile.h"
/* /*
ClientActiveObject ClientActiveObject
@ -68,674 +65,4 @@ void ClientActiveObject::registerType(u16 type, Factory f)
m_types.insert(type, f); m_types.insert(type, f);
} }
/*
TestCAO
*/
// Prototype
TestCAO proto_TestCAO;
TestCAO::TestCAO():
ClientActiveObject(0),
m_node(NULL),
m_position(v3f(0,10*BS,0))
{
ClientActiveObject::registerType(getType(), create);
}
TestCAO::~TestCAO()
{
}
ClientActiveObject* TestCAO::create()
{
return new TestCAO();
}
void TestCAO::addToScene(scene::ISceneManager *smgr)
{
if(m_node != NULL)
return;
video::IVideoDriver* driver = smgr->getVideoDriver();
scene::SMesh *mesh = new scene::SMesh();
scene::IMeshBuffer *buf = new scene::SMeshBuffer();
video::SColor c(255,255,255,255);
video::S3DVertex vertices[4] =
{
video::S3DVertex(-BS/2,-BS/4,0, 0,0,0, c, 0,1),
video::S3DVertex(BS/2,-BS/4,0, 0,0,0, c, 1,1),
video::S3DVertex(BS/2,BS/4,0, 0,0,0, c, 1,0),
video::S3DVertex(-BS/2,BS/4,0, 0,0,0, c, 0,0),
};
u16 indices[] = {0,1,2,2,3,0};
buf->append(vertices, 4, indices, 6);
// Set material
buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
buf->getMaterial().setFlag(video::EMF_BACK_FACE_CULLING, false);
buf->getMaterial().setTexture
(0, driver->getTexture(getTexturePath("rat.png").c_str()));
buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
buf->getMaterial().setFlag(video::EMF_FOG_ENABLE, true);
buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
// Add to mesh
mesh->addMeshBuffer(buf);
buf->drop();
m_node = smgr->addMeshSceneNode(mesh, NULL);
mesh->drop();
updateNodePos();
}
void TestCAO::removeFromScene()
{
if(m_node == NULL)
return;
m_node->remove();
m_node = NULL;
}
void TestCAO::updateLight(u8 light_at_pos)
{
}
v3s16 TestCAO::getLightPosition()
{
return floatToInt(m_position, BS);
}
void TestCAO::updateNodePos()
{
if(m_node == NULL)
return;
m_node->setPosition(m_position);
//m_node->setRotation(v3f(0, 45, 0));
}
void TestCAO::step(float dtime, ClientEnvironment *env)
{
if(m_node)
{
v3f rot = m_node->getRotation();
//dstream<<"dtime="<<dtime<<", rot.Y="<<rot.Y<<std::endl;
rot.Y += dtime * 180;
m_node->setRotation(rot);
}
}
void TestCAO::processMessage(const std::string &data)
{
dstream<<"TestCAO: Got data: "<<data<<std::endl;
std::istringstream is(data, std::ios::binary);
u16 cmd;
is>>cmd;
if(cmd == 0)
{
v3f newpos;
is>>newpos.X;
is>>newpos.Y;
is>>newpos.Z;
m_position = newpos;
updateNodePos();
}
}
/*
ItemCAO
*/
#include "inventory.h"
// Prototype
ItemCAO proto_ItemCAO;
ItemCAO::ItemCAO():
ClientActiveObject(0),
m_selection_box(-BS/3.,0.0,-BS/3., BS/3.,BS*2./3.,BS/3.),
m_node(NULL),
m_position(v3f(0,10*BS,0))
{
ClientActiveObject::registerType(getType(), create);
}
ItemCAO::~ItemCAO()
{
}
ClientActiveObject* ItemCAO::create()
{
return new ItemCAO();
}
void ItemCAO::addToScene(scene::ISceneManager *smgr)
{
if(m_node != NULL)
return;
video::IVideoDriver* driver = smgr->getVideoDriver();
scene::SMesh *mesh = new scene::SMesh();
scene::IMeshBuffer *buf = new scene::SMeshBuffer();
video::SColor c(255,255,255,255);
video::S3DVertex vertices[4] =
{
/*video::S3DVertex(-BS/2,-BS/4,0, 0,0,0, c, 0,1),
video::S3DVertex(BS/2,-BS/4,0, 0,0,0, c, 1,1),
video::S3DVertex(BS/2,BS/4,0, 0,0,0, c, 1,0),
video::S3DVertex(-BS/2,BS/4,0, 0,0,0, c, 0,0),*/
video::S3DVertex(BS/3.,0,0, 0,0,0, c, 0,1),
video::S3DVertex(-BS/3.,0,0, 0,0,0, c, 1,1),
video::S3DVertex(-BS/3.,0+BS*2./3.,0, 0,0,0, c, 1,0),
video::S3DVertex(BS/3.,0+BS*2./3.,0, 0,0,0, c, 0,0),
};
u16 indices[] = {0,1,2,2,3,0};
buf->append(vertices, 4, indices, 6);
// Set material
buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
buf->getMaterial().setFlag(video::EMF_BACK_FACE_CULLING, false);
//buf->getMaterial().setTexture(0, NULL);
// Initialize with the stick texture
buf->getMaterial().setTexture
(0, driver->getTexture(getTexturePath("stick.png").c_str()));
buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
buf->getMaterial().setFlag(video::EMF_FOG_ENABLE, true);
buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
// Add to mesh
mesh->addMeshBuffer(buf);
buf->drop();
m_node = smgr->addMeshSceneNode(mesh, NULL);
mesh->drop();
// Set it to use the materials of the meshbuffers directly.
// This is needed for changing the texture in the future
m_node->setReadOnlyMaterials(true);
updateNodePos();
}
void ItemCAO::removeFromScene()
{
if(m_node == NULL)
return;
m_node->remove();
m_node = NULL;
}
void ItemCAO::updateLight(u8 light_at_pos)
{
if(m_node == NULL)
return;
u8 li = decode_light(light_at_pos);
video::SColor color(255,li,li,li);
scene::IMesh *mesh = m_node->getMesh();
if(mesh == NULL)
return;
u16 mc = mesh->getMeshBufferCount();
for(u16 j=0; j<mc; j++)
{
scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices();
u16 vc = buf->getVertexCount();
for(u16 i=0; i<vc; i++)
{
vertices[i].Color = color;
}
}
}
v3s16 ItemCAO::getLightPosition()
{
return floatToInt(m_position, BS);
}
void ItemCAO::updateNodePos()
{
if(m_node == NULL)
return;
m_node->setPosition(m_position);
}
void ItemCAO::step(float dtime, ClientEnvironment *env)
{
if(m_node)
{
/*v3f rot = m_node->getRotation();
rot.Y += dtime * 120;
m_node->setRotation(rot);*/
LocalPlayer *player = env->getLocalPlayer();
assert(player);
v3f rot = m_node->getRotation();
rot.Y = 180.0 - (player->getYaw());
m_node->setRotation(rot);
}
}
void ItemCAO::processMessage(const std::string &data)
{
dstream<<"ItemCAO: Got message"<<std::endl;
std::istringstream is(data, std::ios::binary);
// command
u8 cmd = readU8(is);
if(cmd == 0)
{
// pos
m_position = readV3F1000(is);
updateNodePos();
}
}
void ItemCAO::initialize(const std::string &data)
{
dstream<<"ItemCAO: Got init data"<<std::endl;
{
std::istringstream is(data, std::ios::binary);
// version
u8 version = readU8(is);
// check version
if(version != 0)
return;
// pos
m_position = readV3F1000(is);
// inventorystring
m_inventorystring = deSerializeString(is);
}
updateNodePos();
/*
Update image of node
*/
if(m_node == NULL)
return;
scene::IMesh *mesh = m_node->getMesh();
if(mesh == NULL)
return;
scene::IMeshBuffer *buf = mesh->getMeshBuffer(0);
if(buf == NULL)
return;
// Create an inventory item to see what is its image
std::istringstream is(m_inventorystring, std::ios_base::binary);
video::ITexture *texture = NULL;
try{
InventoryItem *item = NULL;
item = InventoryItem::deSerialize(is);
dstream<<__FUNCTION_NAME<<": m_inventorystring=\""
<<m_inventorystring<<"\" -> item="<<item
<<std::endl;
if(item)
{
texture = item->getImage();
delete item;
}
}
catch(SerializationError &e)
{
dstream<<"WARNING: "<<__FUNCTION_NAME
<<": error deSerializing inventorystring \""
<<m_inventorystring<<"\""<<std::endl;
}
// Set meshbuffer texture
buf->getMaterial().setTexture(0, texture);
}
/*
RatCAO
*/
#include "inventory.h"
// Prototype
RatCAO proto_RatCAO;
RatCAO::RatCAO():
ClientActiveObject(0),
m_selection_box(-BS/3.,0.0,-BS/3., BS/3.,BS/2.,BS/3.),
m_node(NULL),
m_position(v3f(0,10*BS,0)),
m_yaw(0)
{
ClientActiveObject::registerType(getType(), create);
}
RatCAO::~RatCAO()
{
}
ClientActiveObject* RatCAO::create()
{
return new RatCAO();
}
void RatCAO::addToScene(scene::ISceneManager *smgr)
{
if(m_node != NULL)
return;
video::IVideoDriver* driver = smgr->getVideoDriver();
scene::SMesh *mesh = new scene::SMesh();
scene::IMeshBuffer *buf = new scene::SMeshBuffer();
video::SColor c(255,255,255,255);
video::S3DVertex vertices[4] =
{
video::S3DVertex(-BS/2,0,0, 0,0,0, c, 0,1),
video::S3DVertex(BS/2,0,0, 0,0,0, c, 1,1),
video::S3DVertex(BS/2,BS/2,0, 0,0,0, c, 1,0),
video::S3DVertex(-BS/2,BS/2,0, 0,0,0, c, 0,0),
};
u16 indices[] = {0,1,2,2,3,0};
buf->append(vertices, 4, indices, 6);
// Set material
buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
buf->getMaterial().setFlag(video::EMF_BACK_FACE_CULLING, false);
//buf->getMaterial().setTexture(0, NULL);
buf->getMaterial().setTexture
(0, driver->getTexture(getTexturePath("rat.png").c_str()));
buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
buf->getMaterial().setFlag(video::EMF_FOG_ENABLE, true);
buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
// Add to mesh
mesh->addMeshBuffer(buf);
buf->drop();
m_node = smgr->addMeshSceneNode(mesh, NULL);
mesh->drop();
// Set it to use the materials of the meshbuffers directly.
// This is needed for changing the texture in the future
m_node->setReadOnlyMaterials(true);
updateNodePos();
}
void RatCAO::removeFromScene()
{
if(m_node == NULL)
return;
m_node->remove();
m_node = NULL;
}
void RatCAO::updateLight(u8 light_at_pos)
{
if(m_node == NULL)
return;
u8 li = decode_light(light_at_pos);
video::SColor color(255,li,li,li);
scene::IMesh *mesh = m_node->getMesh();
if(mesh == NULL)
return;
u16 mc = mesh->getMeshBufferCount();
for(u16 j=0; j<mc; j++)
{
scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices();
u16 vc = buf->getVertexCount();
for(u16 i=0; i<vc; i++)
{
vertices[i].Color = color;
}
}
}
v3s16 RatCAO::getLightPosition()
{
return floatToInt(m_position+v3f(0,BS*0.5,0), BS);
}
void RatCAO::updateNodePos()
{
if(m_node == NULL)
return;
//m_node->setPosition(m_position);
m_node->setPosition(pos_translator.vect_show);
v3f rot = m_node->getRotation();
rot.Y = 180.0 - m_yaw;
m_node->setRotation(rot);
}
void RatCAO::step(float dtime, ClientEnvironment *env)
{
pos_translator.translate(dtime);
updateNodePos();
}
void RatCAO::processMessage(const std::string &data)
{
//dstream<<"RatCAO: Got message"<<std::endl;
std::istringstream is(data, std::ios::binary);
// command
u8 cmd = readU8(is);
if(cmd == 0)
{
// pos
m_position = readV3F1000(is);
pos_translator.update(m_position);
// yaw
m_yaw = readF1000(is);
updateNodePos();
}
}
void RatCAO::initialize(const std::string &data)
{
//dstream<<"RatCAO: Got init data"<<std::endl;
{
std::istringstream is(data, std::ios::binary);
// version
u8 version = readU8(is);
// check version
if(version != 0)
return;
// pos
m_position = readV3F1000(is);
pos_translator.init(m_position);
}
updateNodePos();
}
/*
Oerkki1CAO
*/
#include "inventory.h"
// Prototype
Oerkki1CAO proto_Oerkki1CAO;
Oerkki1CAO::Oerkki1CAO():
ClientActiveObject(0),
m_selection_box(-BS/3.,0.0,-BS/3., BS/3.,BS*2.,BS/3.),
m_node(NULL),
m_position(v3f(0,10*BS,0)),
m_yaw(0)
{
ClientActiveObject::registerType(getType(), create);
}
Oerkki1CAO::~Oerkki1CAO()
{
}
ClientActiveObject* Oerkki1CAO::create()
{
return new Oerkki1CAO();
}
void Oerkki1CAO::addToScene(scene::ISceneManager *smgr)
{
if(m_node != NULL)
return;
video::IVideoDriver* driver = smgr->getVideoDriver();
scene::SMesh *mesh = new scene::SMesh();
scene::IMeshBuffer *buf = new scene::SMeshBuffer();
video::SColor c(255,255,255,255);
video::S3DVertex vertices[4] =
{
video::S3DVertex(-BS/2,0,0, 0,0,0, c, 0,1),
video::S3DVertex(BS/2,0,0, 0,0,0, c, 1,1),
video::S3DVertex(BS/2,BS*2,0, 0,0,0, c, 1,0),
video::S3DVertex(-BS/2,BS*2,0, 0,0,0, c, 0,0),
};
u16 indices[] = {0,1,2,2,3,0};
buf->append(vertices, 4, indices, 6);
// Set material
buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
buf->getMaterial().setFlag(video::EMF_BACK_FACE_CULLING, false);
//buf->getMaterial().setTexture(0, NULL);
buf->getMaterial().setTexture
(0, driver->getTexture(getTexturePath("oerkki1.png").c_str()));
buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
buf->getMaterial().setFlag(video::EMF_FOG_ENABLE, true);
buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
// Add to mesh
mesh->addMeshBuffer(buf);
buf->drop();
m_node = smgr->addMeshSceneNode(mesh, NULL);
mesh->drop();
// Set it to use the materials of the meshbuffers directly.
// This is needed for changing the texture in the future
m_node->setReadOnlyMaterials(true);
updateNodePos();
}
void Oerkki1CAO::removeFromScene()
{
if(m_node == NULL)
return;
m_node->remove();
m_node = NULL;
}
void Oerkki1CAO::updateLight(u8 light_at_pos)
{
if(m_node == NULL)
return;
if(light_at_pos <= 2)
{
m_node->setVisible(false);
return;
}
m_node->setVisible(true);
u8 li = decode_light(light_at_pos);
video::SColor color(255,li,li,li);
scene::IMesh *mesh = m_node->getMesh();
if(mesh == NULL)
return;
u16 mc = mesh->getMeshBufferCount();
for(u16 j=0; j<mc; j++)
{
scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices();
u16 vc = buf->getVertexCount();
for(u16 i=0; i<vc; i++)
{
vertices[i].Color = color;
}
}
}
v3s16 Oerkki1CAO::getLightPosition()
{
return floatToInt(m_position+v3f(0,BS*1.5,0), BS);
}
void Oerkki1CAO::updateNodePos()
{
if(m_node == NULL)
return;
//m_node->setPosition(m_position);
m_node->setPosition(pos_translator.vect_show);
v3f rot = m_node->getRotation();
rot.Y = 180.0 - m_yaw + 90.0;
m_node->setRotation(rot);
}
void Oerkki1CAO::step(float dtime, ClientEnvironment *env)
{
pos_translator.translate(dtime);
updateNodePos();
LocalPlayer *player = env->getLocalPlayer();
assert(player);
v3f playerpos = player->getPosition();
v2f playerpos_2d(playerpos.X,playerpos.Z);
v2f objectpos_2d(m_position.X,m_position.Z);
if(fabs(m_position.Y - playerpos.Y) < 3.0*BS &&
objectpos_2d.getDistanceFrom(playerpos_2d) < 1.0*BS)
{
if(m_attack_interval.step(dtime, 0.5))
{
env->damageLocalPlayer(2);
}
}
}
void Oerkki1CAO::processMessage(const std::string &data)
{
//dstream<<"Oerkki1CAO: Got message"<<std::endl;
std::istringstream is(data, std::ios::binary);
// command
u8 cmd = readU8(is);
if(cmd == 0)
{
// pos
m_position = readV3F1000(is);
pos_translator.update(m_position);
// yaw
m_yaw = readF1000(is);
updateNodePos();
}
}
void Oerkki1CAO::initialize(const std::string &data)
{
//dstream<<"Oerkki1CAO: Got init data"<<std::endl;
{
std::istringstream is(data, std::ios::binary);
// version
u8 version = readU8(is);
// check version
if(version != 0)
return;
// pos
m_position = readV3F1000(is);
pos_translator.init(m_position);
}
updateNodePos();
}

@ -22,7 +22,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "common_irrlicht.h" #include "common_irrlicht.h"
#include "activeobject.h" #include "activeobject.h"
#include "utility.h"
/* /*
@ -36,63 +35,6 @@ Some planning
*/ */
/*
SmoothTranslator
*/
struct SmoothTranslator
{
v3f vect_old;
f32 anim_counter;
f32 anim_time;
f32 anim_time_counter;
v3f vect_show;
v3f vect_aim;
SmoothTranslator():
vect_old(0,0,0),
anim_counter(0),
anim_time(0),
anim_time_counter(0),
vect_show(0,0,0),
vect_aim(0,0,0)
{}
void init(v3f vect)
{
vect_old = vect;
vect_show = vect;
vect_aim = vect;
}
void update(v3f vect_new)
{
vect_old = vect_show;
vect_aim = vect_new;
if(anim_time < 0.001 || anim_time > 1.0)
anim_time = anim_time_counter;
else
anim_time = anim_time * 0.9 + anim_time_counter * 0.1;
anim_time_counter = 0;
anim_counter = 0;
}
void translate(f32 dtime)
{
anim_time_counter = anim_time_counter + dtime;
anim_counter = anim_counter + dtime;
v3f vect_move = vect_aim - vect_old;
f32 moveratio = 1.0;
if(anim_time > 0.001)
moveratio = anim_time_counter / anim_time;
// Move a bit less than should, to avoid oscillation
moveratio = moveratio * 0.8;
if(moveratio > 1.5)
moveratio = 1.5;
vect_show = vect_old + vect_move * moveratio;
}
};
class ClientEnvironment; class ClientEnvironment;
class ClientActiveObject : public ActiveObject class ClientActiveObject : public ActiveObject
@ -153,164 +95,5 @@ struct DistanceSortedActiveObject
} }
}; };
/*
TestCAO
*/
class TestCAO : public ClientActiveObject
{
public:
TestCAO();
virtual ~TestCAO();
u8 getType() const
{
return ACTIVEOBJECT_TYPE_TEST;
}
static ClientActiveObject* create();
void addToScene(scene::ISceneManager *smgr);
void removeFromScene();
void updateLight(u8 light_at_pos);
v3s16 getLightPosition();
void updateNodePos();
void step(float dtime, ClientEnvironment *env);
void processMessage(const std::string &data);
private:
scene::IMeshSceneNode *m_node;
v3f m_position;
};
/*
ItemCAO
*/
class ItemCAO : public ClientActiveObject
{
public:
ItemCAO();
virtual ~ItemCAO();
u8 getType() const
{
return ACTIVEOBJECT_TYPE_ITEM;
}
static ClientActiveObject* create();
void addToScene(scene::ISceneManager *smgr);
void removeFromScene();
void updateLight(u8 light_at_pos);
v3s16 getLightPosition();
void updateNodePos();
void step(float dtime, ClientEnvironment *env);
void processMessage(const std::string &data);
void initialize(const std::string &data);
core::aabbox3d<f32>* getSelectionBox()
{return &m_selection_box;}
v3f getPosition()
{return m_position;}
private:
core::aabbox3d<f32> m_selection_box;
scene::IMeshSceneNode *m_node;
v3f m_position;
std::string m_inventorystring;
};
/*
RatCAO
*/
class RatCAO : public ClientActiveObject
{
public:
RatCAO();
virtual ~RatCAO();
u8 getType() const
{
return ACTIVEOBJECT_TYPE_RAT;
}
static ClientActiveObject* create();
void addToScene(scene::ISceneManager *smgr);
void removeFromScene();
void updateLight(u8 light_at_pos);
v3s16 getLightPosition();
void updateNodePos();
void step(float dtime, ClientEnvironment *env);
void processMessage(const std::string &data);
void initialize(const std::string &data);
core::aabbox3d<f32>* getSelectionBox()
{return &m_selection_box;}
v3f getPosition()
{return m_position;}
private:
core::aabbox3d<f32> m_selection_box;
scene::IMeshSceneNode *m_node;
v3f m_position;
float m_yaw;
SmoothTranslator pos_translator;
};
/*
Oerkki1CAO
*/
class Oerkki1CAO : public ClientActiveObject
{
public:
Oerkki1CAO();
virtual ~Oerkki1CAO();
u8 getType() const
{
return ACTIVEOBJECT_TYPE_OERKKI1;
}
static ClientActiveObject* create();
void addToScene(scene::ISceneManager *smgr);
void removeFromScene();
void updateLight(u8 light_at_pos);
v3s16 getLightPosition();
void updateNodePos();
void step(float dtime, ClientEnvironment *env);
void processMessage(const std::string &data);
void initialize(const std::string &data);
core::aabbox3d<f32>* getSelectionBox()
{return &m_selection_box;}
v3f getPosition()
{return pos_translator.vect_show;}
//{return m_position;}
private:
IntervalLimiter m_attack_interval;
core::aabbox3d<f32> m_selection_box;
scene::IMeshSceneNode *m_node;
v3f m_position;
float m_yaw;
SmoothTranslator pos_translator;
};
#endif #endif

@ -37,7 +37,7 @@ enum ToClientCommand
[0] u16 TOSERVER_INIT [0] u16 TOSERVER_INIT
[2] u8 deployed version [2] u8 deployed version
[3] v3s16 player's position + v3f(0,BS/2,0) floatToInt'd [3] v3s16 player's position + v3f(0,BS/2,0) floatToInt'd
([4] u64 map seed (new as of 2011-02-27)) [12] u64 map seed (new as of 2011-02-27)
NOTE: The position in here is deprecated; position is NOTE: The position in here is deprecated; position is
explicitly sent afterwards explicitly sent afterwards

@ -182,4 +182,58 @@ collisionMoveResult collisionMoveSimple(Map *map, f32 pos_max_d,
return result; return result;
} }
collisionMoveResult collisionMovePrecise(Map *map, f32 pos_max_d,
const core::aabbox3d<f32> &box_0,
f32 dtime, v3f &pos_f, v3f &speed_f)
{
collisionMoveResult final_result;
// Maximum time increment (for collision detection etc)
// time = distance / speed
f32 dtime_max_increment = pos_max_d / speed_f.getLength();
// Maximum time increment is 10ms or lower
if(dtime_max_increment > 0.01)
dtime_max_increment = 0.01;
// Don't allow overly huge dtime
if(dtime > 2.0)
dtime = 2.0;
f32 dtime_downcount = dtime;
u32 loopcount = 0;
do
{
loopcount++;
f32 dtime_part;
if(dtime_downcount > dtime_max_increment)
{
dtime_part = dtime_max_increment;
dtime_downcount -= dtime_part;
}
else
{
dtime_part = dtime_downcount;
/*
Setting this to 0 (no -=dtime_part) disables an infinite loop
when dtime_part is so small that dtime_downcount -= dtime_part
does nothing
*/
dtime_downcount = 0;
}
collisionMoveResult result = collisionMoveSimple(map, pos_max_d,
box_0, dtime_part, pos_f, speed_f);
if(result.touching_ground)
final_result.touching_ground = true;
}
while(dtime_downcount > 0.001);
return final_result;
}

@ -33,10 +33,15 @@ struct collisionMoveResult
{} {}
}; };
// Moves using a single iteration; speed should not exceed pos_max_d/dtime
collisionMoveResult collisionMoveSimple(Map *map, f32 pos_max_d, collisionMoveResult collisionMoveSimple(Map *map, f32 pos_max_d,
const core::aabbox3d<f32> &box_0, const core::aabbox3d<f32> &box_0,
f32 dtime, v3f &pos_f, v3f &speed_f); f32 dtime, v3f &pos_f, v3f &speed_f);
//{return collisionMoveResult();}
// Moves using as many iterations as needed
collisionMoveResult collisionMovePrecise(Map *map, f32 pos_max_d,
const core::aabbox3d<f32> &box_0,
f32 dtime, v3f &pos_f, v3f &speed_f);
enum CollisionType enum CollisionType
{ {

753
src/content_cao.cpp Normal file

@ -0,0 +1,753 @@
/*
Minetest-c55
Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "content_cao.h"
#include "tile.h"
#include "environment.h"
/*
TestCAO
*/
// Prototype
TestCAO proto_TestCAO;
TestCAO::TestCAO():
ClientActiveObject(0),
m_node(NULL),
m_position(v3f(0,10*BS,0))
{
ClientActiveObject::registerType(getType(), create);
}
TestCAO::~TestCAO()
{
}
ClientActiveObject* TestCAO::create()
{
return new TestCAO();
}
void TestCAO::addToScene(scene::ISceneManager *smgr)
{
if(m_node != NULL)
return;
video::IVideoDriver* driver = smgr->getVideoDriver();
scene::SMesh *mesh = new scene::SMesh();
scene::IMeshBuffer *buf = new scene::SMeshBuffer();
video::SColor c(255,255,255,255);
video::S3DVertex vertices[4] =
{
video::S3DVertex(-BS/2,-BS/4,0, 0,0,0, c, 0,1),
video::S3DVertex(BS/2,-BS/4,0, 0,0,0, c, 1,1),
video::S3DVertex(BS/2,BS/4,0, 0,0,0, c, 1,0),
video::S3DVertex(-BS/2,BS/4,0, 0,0,0, c, 0,0),
};
u16 indices[] = {0,1,2,2,3,0};
buf->append(vertices, 4, indices, 6);
// Set material
buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
buf->getMaterial().setFlag(video::EMF_BACK_FACE_CULLING, false);
buf->getMaterial().setTexture
(0, driver->getTexture(getTexturePath("rat.png").c_str()));
buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
buf->getMaterial().setFlag(video::EMF_FOG_ENABLE, true);
buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
// Add to mesh
mesh->addMeshBuffer(buf);
buf->drop();
m_node = smgr->addMeshSceneNode(mesh, NULL);
mesh->drop();
updateNodePos();
}
void TestCAO::removeFromScene()
{
if(m_node == NULL)
return;
m_node->remove();
m_node = NULL;
}
void TestCAO::updateLight(u8 light_at_pos)
{
}
v3s16 TestCAO::getLightPosition()
{
return floatToInt(m_position, BS);
}
void TestCAO::updateNodePos()
{
if(m_node == NULL)
return;
m_node->setPosition(m_position);
//m_node->setRotation(v3f(0, 45, 0));
}
void TestCAO::step(float dtime, ClientEnvironment *env)
{
if(m_node)
{
v3f rot = m_node->getRotation();
//dstream<<"dtime="<<dtime<<", rot.Y="<<rot.Y<<std::endl;
rot.Y += dtime * 180;
m_node->setRotation(rot);
}
}
void TestCAO::processMessage(const std::string &data)
{
dstream<<"TestCAO: Got data: "<<data<<std::endl;
std::istringstream is(data, std::ios::binary);
u16 cmd;
is>>cmd;
if(cmd == 0)
{
v3f newpos;
is>>newpos.X;
is>>newpos.Y;
is>>newpos.Z;
m_position = newpos;
updateNodePos();
}
}
/*
ItemCAO
*/
#include "inventory.h"
// Prototype
ItemCAO proto_ItemCAO;
ItemCAO::ItemCAO():
ClientActiveObject(0),
m_selection_box(-BS/3.,0.0,-BS/3., BS/3.,BS*2./3.,BS/3.),
m_node(NULL),
m_position(v3f(0,10*BS,0))
{
ClientActiveObject::registerType(getType(), create);
}
ItemCAO::~ItemCAO()
{
}
ClientActiveObject* ItemCAO::create()
{
return new ItemCAO();
}
void ItemCAO::addToScene(scene::ISceneManager *smgr)
{
if(m_node != NULL)
return;
video::IVideoDriver* driver = smgr->getVideoDriver();
scene::SMesh *mesh = new scene::SMesh();
scene::IMeshBuffer *buf = new scene::SMeshBuffer();
video::SColor c(255,255,255,255);
video::S3DVertex vertices[4] =
{
/*video::S3DVertex(-BS/2,-BS/4,0, 0,0,0, c, 0,1),
video::S3DVertex(BS/2,-BS/4,0, 0,0,0, c, 1,1),
video::S3DVertex(BS/2,BS/4,0, 0,0,0, c, 1,0),
video::S3DVertex(-BS/2,BS/4,0, 0,0,0, c, 0,0),*/
video::S3DVertex(BS/3.,0,0, 0,0,0, c, 0,1),
video::S3DVertex(-BS/3.,0,0, 0,0,0, c, 1,1),
video::S3DVertex(-BS/3.,0+BS*2./3.,0, 0,0,0, c, 1,0),
video::S3DVertex(BS/3.,0+BS*2./3.,0, 0,0,0, c, 0,0),
};
u16 indices[] = {0,1,2,2,3,0};
buf->append(vertices, 4, indices, 6);
// Set material
buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
buf->getMaterial().setFlag(video::EMF_BACK_FACE_CULLING, false);
//buf->getMaterial().setTexture(0, NULL);
// Initialize with the stick texture
buf->getMaterial().setTexture
(0, driver->getTexture(getTexturePath("stick.png").c_str()));
buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
buf->getMaterial().setFlag(video::EMF_FOG_ENABLE, true);
buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
// Add to mesh
mesh->addMeshBuffer(buf);
buf->drop();
m_node = smgr->addMeshSceneNode(mesh, NULL);
mesh->drop();
// Set it to use the materials of the meshbuffers directly.
// This is needed for changing the texture in the future
m_node->setReadOnlyMaterials(true);
updateNodePos();
}
void ItemCAO::removeFromScene()
{
if(m_node == NULL)
return;
m_node->remove();
m_node = NULL;
}
void ItemCAO::updateLight(u8 light_at_pos)
{
if(m_node == NULL)
return;
u8 li = decode_light(light_at_pos);
video::SColor color(255,li,li,li);
scene::IMesh *mesh = m_node->getMesh();
if(mesh == NULL)
return;
u16 mc = mesh->getMeshBufferCount();
for(u16 j=0; j<mc; j++)
{
scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices();
u16 vc = buf->getVertexCount();
for(u16 i=0; i<vc; i++)
{
vertices[i].Color = color;
}
}
}
v3s16 ItemCAO::getLightPosition()
{
return floatToInt(m_position, BS);
}
void ItemCAO::updateNodePos()
{
if(m_node == NULL)
return;
m_node->setPosition(m_position);
}
void ItemCAO::step(float dtime, ClientEnvironment *env)
{
if(m_node)
{
/*v3f rot = m_node->getRotation();
rot.Y += dtime * 120;
m_node->setRotation(rot);*/
LocalPlayer *player = env->getLocalPlayer();
assert(player);
v3f rot = m_node->getRotation();
rot.Y = 180.0 - (player->getYaw());
m_node->setRotation(rot);
}
}
void ItemCAO::processMessage(const std::string &data)
{
dstream<<"ItemCAO: Got message"<<std::endl;
std::istringstream is(data, std::ios::binary);
// command
u8 cmd = readU8(is);
if(cmd == 0)
{
// pos
m_position = readV3F1000(is);
updateNodePos();
}
}
void ItemCAO::initialize(const std::string &data)
{
dstream<<"ItemCAO: Got init data"<<std::endl;
{
std::istringstream is(data, std::ios::binary);
// version
u8 version = readU8(is);
// check version
if(version != 0)
return;
// pos
m_position = readV3F1000(is);
// inventorystring
m_inventorystring = deSerializeString(is);
}
updateNodePos();
/*
Update image of node
*/
if(m_node == NULL)
return;
scene::IMesh *mesh = m_node->getMesh();
if(mesh == NULL)
return;
scene::IMeshBuffer *buf = mesh->getMeshBuffer(0);
if(buf == NULL)
return;
// Create an inventory item to see what is its image
std::istringstream is(m_inventorystring, std::ios_base::binary);
video::ITexture *texture = NULL;
try{
InventoryItem *item = NULL;
item = InventoryItem::deSerialize(is);
dstream<<__FUNCTION_NAME<<": m_inventorystring=\""
<<m_inventorystring<<"\" -> item="<<item
<<std::endl;
if(item)
{
texture = item->getImage();
delete item;
}
}
catch(SerializationError &e)
{
dstream<<"WARNING: "<<__FUNCTION_NAME
<<": error deSerializing inventorystring \""
<<m_inventorystring<<"\""<<std::endl;
}
// Set meshbuffer texture
buf->getMaterial().setTexture(0, texture);
}
/*
RatCAO
*/
#include "inventory.h"
// Prototype
RatCAO proto_RatCAO;
RatCAO::RatCAO():
ClientActiveObject(0),
m_selection_box(-BS/3.,0.0,-BS/3., BS/3.,BS/2.,BS/3.),
m_node(NULL),
m_position(v3f(0,10*BS,0)),
m_yaw(0)
{
ClientActiveObject::registerType(getType(), create);
}
RatCAO::~RatCAO()
{
}
ClientActiveObject* RatCAO::create()
{
return new RatCAO();
}
void RatCAO::addToScene(scene::ISceneManager *smgr)
{
if(m_node != NULL)
return;
video::IVideoDriver* driver = smgr->getVideoDriver();
scene::SMesh *mesh = new scene::SMesh();
scene::IMeshBuffer *buf = new scene::SMeshBuffer();
video::SColor c(255,255,255,255);
video::S3DVertex vertices[4] =
{
video::S3DVertex(-BS/2,0,0, 0,0,0, c, 0,1),
video::S3DVertex(BS/2,0,0, 0,0,0, c, 1,1),
video::S3DVertex(BS/2,BS/2,0, 0,0,0, c, 1,0),
video::S3DVertex(-BS/2,BS/2,0, 0,0,0, c, 0,0),
};
u16 indices[] = {0,1,2,2,3,0};
buf->append(vertices, 4, indices, 6);
// Set material
buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
buf->getMaterial().setFlag(video::EMF_BACK_FACE_CULLING, false);
//buf->getMaterial().setTexture(0, NULL);
buf->getMaterial().setTexture
(0, driver->getTexture(getTexturePath("rat.png").c_str()));
buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
buf->getMaterial().setFlag(video::EMF_FOG_ENABLE, true);
buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
// Add to mesh
mesh->addMeshBuffer(buf);
buf->drop();
m_node = smgr->addMeshSceneNode(mesh, NULL);
mesh->drop();
// Set it to use the materials of the meshbuffers directly.
// This is needed for changing the texture in the future
m_node->setReadOnlyMaterials(true);
updateNodePos();
}
void RatCAO::removeFromScene()
{
if(m_node == NULL)
return;
m_node->remove();
m_node = NULL;
}
void RatCAO::updateLight(u8 light_at_pos)
{
if(m_node == NULL)
return;
u8 li = decode_light(light_at_pos);
video::SColor color(255,li,li,li);
scene::IMesh *mesh = m_node->getMesh();
if(mesh == NULL)
return;
u16 mc = mesh->getMeshBufferCount();
for(u16 j=0; j<mc; j++)
{
scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices();
u16 vc = buf->getVertexCount();
for(u16 i=0; i<vc; i++)
{
vertices[i].Color = color;
}
}
}
v3s16 RatCAO::getLightPosition()
{
return floatToInt(m_position+v3f(0,BS*0.5,0), BS);
}
void RatCAO::updateNodePos()
{
if(m_node == NULL)
return;
//m_node->setPosition(m_position);
m_node->setPosition(pos_translator.vect_show);
v3f rot = m_node->getRotation();
rot.Y = 180.0 - m_yaw;
m_node->setRotation(rot);
}
void RatCAO::step(float dtime, ClientEnvironment *env)
{
pos_translator.translate(dtime);
updateNodePos();
}
void RatCAO::processMessage(const std::string &data)
{
//dstream<<"RatCAO: Got message"<<std::endl;
std::istringstream is(data, std::ios::binary);
// command
u8 cmd = readU8(is);
if(cmd == 0)
{
// pos
m_position = readV3F1000(is);
pos_translator.update(m_position);
// yaw
m_yaw = readF1000(is);
updateNodePos();
}
}
void RatCAO::initialize(const std::string &data)
{
//dstream<<"RatCAO: Got init data"<<std::endl;
{
std::istringstream is(data, std::ios::binary);
// version
u8 version = readU8(is);
// check version
if(version != 0)
return;
// pos
m_position = readV3F1000(is);
pos_translator.init(m_position);
}
updateNodePos();
}
/*
Oerkki1CAO
*/
#include "inventory.h"
// Prototype
Oerkki1CAO proto_Oerkki1CAO;
Oerkki1CAO::Oerkki1CAO():
ClientActiveObject(0),
m_selection_box(-BS/3.,0.0,-BS/3., BS/3.,BS*2.,BS/3.),
m_node(NULL),
m_position(v3f(0,10*BS,0)),
m_yaw(0),
m_damage_visual_timer(0),
m_damage_texture_enabled(false)
{
ClientActiveObject::registerType(getType(), create);
}
Oerkki1CAO::~Oerkki1CAO()
{
}
ClientActiveObject* Oerkki1CAO::create()
{
return new Oerkki1CAO();
}
void Oerkki1CAO::addToScene(scene::ISceneManager *smgr)
{
if(m_node != NULL)
return;
video::IVideoDriver* driver = smgr->getVideoDriver();
scene::SMesh *mesh = new scene::SMesh();
scene::IMeshBuffer *buf = new scene::SMeshBuffer();
video::SColor c(255,255,255,255);
video::S3DVertex vertices[4] =
{
video::S3DVertex(-BS/2-BS,0,0, 0,0,0, c, 0,1),
video::S3DVertex(BS/2+BS,0,0, 0,0,0, c, 1,1),
video::S3DVertex(BS/2+BS,BS*2,0, 0,0,0, c, 1,0),
video::S3DVertex(-BS/2-BS,BS*2,0, 0,0,0, c, 0,0),
};
u16 indices[] = {0,1,2,2,3,0};
buf->append(vertices, 4, indices, 6);
// Set material
buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
buf->getMaterial().setFlag(video::EMF_BACK_FACE_CULLING, false);
//buf->getMaterial().setTexture(0, NULL);
buf->getMaterial().setTexture
(0, driver->getTexture(getTexturePath("oerkki1.png").c_str()));
buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
buf->getMaterial().setFlag(video::EMF_FOG_ENABLE, true);
buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
// Add to mesh
mesh->addMeshBuffer(buf);
buf->drop();
m_node = smgr->addMeshSceneNode(mesh, NULL);
mesh->drop();
// Set it to use the materials of the meshbuffers directly.
// This is needed for changing the texture in the future
m_node->setReadOnlyMaterials(true);
updateNodePos();
}
void Oerkki1CAO::removeFromScene()
{
if(m_node == NULL)
return;
m_node->remove();
m_node = NULL;
}
void Oerkki1CAO::updateLight(u8 light_at_pos)
{
if(m_node == NULL)
return;
if(light_at_pos <= 2)
{
m_node->setVisible(false);
return;
}
m_node->setVisible(true);
u8 li = decode_light(light_at_pos);
video::SColor color(255,li,li,li);
scene::IMesh *mesh = m_node->getMesh();
if(mesh == NULL)
return;
u16 mc = mesh->getMeshBufferCount();
for(u16 j=0; j<mc; j++)
{
scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices();
u16 vc = buf->getVertexCount();
for(u16 i=0; i<vc; i++)
{
vertices[i].Color = color;
}
}
}
v3s16 Oerkki1CAO::getLightPosition()
{
return floatToInt(m_position+v3f(0,BS*1.5,0), BS);
}
void Oerkki1CAO::updateNodePos()
{
if(m_node == NULL)
return;
//m_node->setPosition(m_position);
m_node->setPosition(pos_translator.vect_show);
v3f rot = m_node->getRotation();
rot.Y = 180.0 - m_yaw + 90.0;
m_node->setRotation(rot);
}
void Oerkki1CAO::step(float dtime, ClientEnvironment *env)
{
pos_translator.translate(dtime);
updateNodePos();
LocalPlayer *player = env->getLocalPlayer();
assert(player);
v3f playerpos = player->getPosition();
v2f playerpos_2d(playerpos.X,playerpos.Z);
v2f objectpos_2d(m_position.X,m_position.Z);
if(fabs(m_position.Y - playerpos.Y) < 3.0*BS &&
objectpos_2d.getDistanceFrom(playerpos_2d) < 1.5*BS)
{
if(m_attack_interval.step(dtime, 0.5))
{
env->damageLocalPlayer(2);
}
}
if(m_damage_visual_timer > 0)
{
if(!m_damage_texture_enabled)
{
// Enable damage texture
if(m_node)
{
video::IVideoDriver* driver =
m_node->getSceneManager()->getVideoDriver();
scene::IMesh *mesh = m_node->getMesh();
if(mesh == NULL)
return;
u16 mc = mesh->getMeshBufferCount();
for(u16 j=0; j<mc; j++)
{
scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
buf->getMaterial().setTexture(0, driver->getTexture(
getTexturePath("oerkki1_damaged.png").c_str()));
}
}
m_damage_texture_enabled = true;
}
m_damage_visual_timer -= dtime;
}
else
{
if(m_damage_texture_enabled)
{
// Disable damage texture
if(m_node)
{
video::IVideoDriver* driver =
m_node->getSceneManager()->getVideoDriver();
scene::IMesh *mesh = m_node->getMesh();
if(mesh == NULL)
return;
u16 mc = mesh->getMeshBufferCount();
for(u16 j=0; j<mc; j++)
{
scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
buf->getMaterial().setTexture(0, driver->getTexture(
getTexturePath("oerkki1.png").c_str()));
}
}
m_damage_texture_enabled = false;
}
}
}
void Oerkki1CAO::processMessage(const std::string &data)
{
//dstream<<"Oerkki1CAO: Got message"<<std::endl;
std::istringstream is(data, std::ios::binary);
// command
u8 cmd = readU8(is);
if(cmd == 0)
{
// pos
m_position = readV3F1000(is);
pos_translator.update(m_position);
// yaw
m_yaw = readF1000(is);
updateNodePos();
}
else if(cmd == 1)
{
u16 damage = readU8(is);
m_damage_visual_timer = 1.0;
}
}
void Oerkki1CAO::initialize(const std::string &data)
{
//dstream<<"Oerkki1CAO: Got init data"<<std::endl;
{
std::istringstream is(data, std::ios::binary);
// version
u8 version = readU8(is);
// check version
if(version != 0)
return;
// pos
m_position = readV3F1000(is);
pos_translator.init(m_position);
}
updateNodePos();
}

248
src/content_cao.h Normal file

@ -0,0 +1,248 @@
/*
Minetest-c55
Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef CONTENT_CAO_HEADER
#define CONTENT_CAO_HEADER
#include "clientobject.h"
#include "content_object.h"
#include "utility.h" // For IntervalLimiter
/*
SmoothTranslator
*/
struct SmoothTranslator
{
v3f vect_old;
f32 anim_counter;
f32 anim_time;
f32 anim_time_counter;
v3f vect_show;
v3f vect_aim;
SmoothTranslator():
vect_old(0,0,0),
anim_counter(0),
anim_time(0),
anim_time_counter(0),
vect_show(0,0,0),
vect_aim(0,0,0)
{}
void init(v3f vect)
{
vect_old = vect;
vect_show = vect;
vect_aim = vect;
}
void update(v3f vect_new)
{
vect_old = vect_show;
vect_aim = vect_new;
if(anim_time < 0.001 || anim_time > 1.0)
anim_time = anim_time_counter;
else
anim_time = anim_time * 0.9 + anim_time_counter * 0.1;
anim_time_counter = 0;
anim_counter = 0;
}
void translate(f32 dtime)
{
anim_time_counter = anim_time_counter + dtime;
anim_counter = anim_counter + dtime;
v3f vect_move = vect_aim - vect_old;
f32 moveratio = 1.0;
if(anim_time > 0.001)
moveratio = anim_time_counter / anim_time;
// Move a bit less than should, to avoid oscillation
moveratio = moveratio * 0.8;
if(moveratio > 1.5)
moveratio = 1.5;
vect_show = vect_old + vect_move * moveratio;
}
};
/*
TestCAO
*/
class TestCAO : public ClientActiveObject
{
public:
TestCAO();
virtual ~TestCAO();
u8 getType() const
{
return ACTIVEOBJECT_TYPE_TEST;
}
static ClientActiveObject* create();
void addToScene(scene::ISceneManager *smgr);
void removeFromScene();
void updateLight(u8 light_at_pos);
v3s16 getLightPosition();
void updateNodePos();
void step(float dtime, ClientEnvironment *env);
void processMessage(const std::string &data);
private:
scene::IMeshSceneNode *m_node;
v3f m_position;
};
/*
ItemCAO
*/
class ItemCAO : public ClientActiveObject
{
public:
ItemCAO();
virtual ~ItemCAO();
u8 getType() const
{
return ACTIVEOBJECT_TYPE_ITEM;
}
static ClientActiveObject* create();
void addToScene(scene::ISceneManager *smgr);
void removeFromScene();
void updateLight(u8 light_at_pos);
v3s16 getLightPosition();
void updateNodePos();
void step(float dtime, ClientEnvironment *env);
void processMessage(const std::string &data);
void initialize(const std::string &data);
core::aabbox3d<f32>* getSelectionBox()
{return &m_selection_box;}
v3f getPosition()
{return m_position;}
private:
core::aabbox3d<f32> m_selection_box;
scene::IMeshSceneNode *m_node;
v3f m_position;
std::string m_inventorystring;
};
/*
RatCAO
*/
class RatCAO : public ClientActiveObject
{
public:
RatCAO();
virtual ~RatCAO();
u8 getType() const
{
return ACTIVEOBJECT_TYPE_RAT;
}
static ClientActiveObject* create();
void addToScene(scene::ISceneManager *smgr);
void removeFromScene();
void updateLight(u8 light_at_pos);
v3s16 getLightPosition();
void updateNodePos();
void step(float dtime, ClientEnvironment *env);
void processMessage(const std::string &data);
void initialize(const std::string &data);
core::aabbox3d<f32>* getSelectionBox()
{return &m_selection_box;}
v3f getPosition()
{return m_position;}
private:
core::aabbox3d<f32> m_selection_box;
scene::IMeshSceneNode *m_node;
v3f m_position;
float m_yaw;
SmoothTranslator pos_translator;
};
/*
Oerkki1CAO
*/
class Oerkki1CAO : public ClientActiveObject
{
public:
Oerkki1CAO();
virtual ~Oerkki1CAO();
u8 getType() const
{
return ACTIVEOBJECT_TYPE_OERKKI1;
}
static ClientActiveObject* create();
void addToScene(scene::ISceneManager *smgr);
void removeFromScene();
void updateLight(u8 light_at_pos);
v3s16 getLightPosition();
void updateNodePos();
void step(float dtime, ClientEnvironment *env);
void processMessage(const std::string &data);
void initialize(const std::string &data);
core::aabbox3d<f32>* getSelectionBox()
{return &m_selection_box;}
v3f getPosition()
{return pos_translator.vect_show;}
//{return m_position;}
private:
IntervalLimiter m_attack_interval;
core::aabbox3d<f32> m_selection_box;
scene::IMeshSceneNode *m_node;
v3f m_position;
float m_yaw;
SmoothTranslator pos_translator;
float m_damage_visual_timer;
bool m_damage_texture_enabled;
};
#endif

@ -19,8 +19,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "content_inventory.h" #include "content_inventory.h"
#include "inventory.h" #include "inventory.h"
#include "serverobject.h"
#include "content_mapnode.h" #include "content_mapnode.h"
//#include "serverobject.h"
#include "content_sao.h"
bool item_material_is_cookable(u8 content) bool item_material_is_cookable(u8 content)
{ {

@ -272,7 +272,7 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
/* /*
Signs on walls Signs on walls
*/ */
if(n.d == CONTENT_SIGN_WALL) else if(n.d == CONTENT_SIGN_WALL)
{ {
u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio)); u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio));
video::SColor c(255,l,l,l); video::SColor c(255,l,l,l);

@ -224,7 +224,6 @@ void content_mapnode_init()
// Deprecated // Deprecated
i = CONTENT_COALSTONE; i = CONTENT_COALSTONE;
f = &content_features(i); f = &content_features(i);
//f->translate_to = new MapNode(CONTENT_STONE, MINERAL_COAL);
f->setAllTextures("stone.png^mineral_coal.png"); f->setAllTextures("stone.png^mineral_coal.png");
f->is_ground_content = true; f->is_ground_content = true;
setStoneLikeDiggingProperties(f->digging_properties, 1.5); setStoneLikeDiggingProperties(f->digging_properties, 1.5);

29
src/content_object.h Normal file

@ -0,0 +1,29 @@
/*
Minetest-c55
Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef CONTENT_OBJECT_HEADER
#define CONTENT_OBJECT_HEADER
#define ACTIVEOBJECT_TYPE_TEST 1
#define ACTIVEOBJECT_TYPE_ITEM 2
#define ACTIVEOBJECT_TYPE_RAT 3
#define ACTIVEOBJECT_TYPE_OERKKI1 4
#endif

694
src/content_sao.cpp Normal file

@ -0,0 +1,694 @@
/*
Minetest-c55
Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "content_sao.h"
#include "collision.h"
#include "environment.h"
/*
TestSAO
*/
// Prototype
TestSAO proto_TestSAO(NULL, 0, v3f(0,0,0));
TestSAO::TestSAO(ServerEnvironment *env, u16 id, v3f pos):
ServerActiveObject(env, id, pos),
m_timer1(0),
m_age(0)
{
ServerActiveObject::registerType(getType(), create);
}
ServerActiveObject* TestSAO::create(ServerEnvironment *env, u16 id, v3f pos,
const std::string &data)
{
return new TestSAO(env, id, pos);
}
void TestSAO::step(float dtime, bool send_recommended)
{
m_age += dtime;
if(m_age > 10)
{
m_removed = true;
return;
}
m_base_position.Y += dtime * BS * 2;
if(m_base_position.Y > 8*BS)
m_base_position.Y = 2*BS;
if(send_recommended == false)
return;
m_timer1 -= dtime;
if(m_timer1 < 0.0)
{
m_timer1 += 0.125;
//dstream<<"TestSAO: id="<<getId()<<" sending data"<<std::endl;
std::string data;
data += itos(0); // 0 = position
data += " ";
data += itos(m_base_position.X);
data += " ";
data += itos(m_base_position.Y);
data += " ";
data += itos(m_base_position.Z);
ActiveObjectMessage aom(getId(), false, data);
m_messages_out.push_back(aom);
}
}
/*
ItemSAO
*/
// Prototype
ItemSAO proto_ItemSAO(NULL, 0, v3f(0,0,0), "");
ItemSAO::ItemSAO(ServerEnvironment *env, u16 id, v3f pos,
const std::string inventorystring):
ServerActiveObject(env, id, pos),
m_inventorystring(inventorystring),
m_speed_f(0,0,0),
m_last_sent_position(0,0,0)
{
ServerActiveObject::registerType(getType(), create);
}
ServerActiveObject* ItemSAO::create(ServerEnvironment *env, u16 id, v3f pos,
const std::string &data)
{
std::istringstream is(data, std::ios::binary);
char buf[1];
// read version
is.read(buf, 1);
u8 version = buf[0];
// check if version is supported
if(version != 0)
return NULL;
std::string inventorystring = deSerializeString(is);
dstream<<"ItemSAO::create(): Creating item \""
<<inventorystring<<"\""<<std::endl;
return new ItemSAO(env, id, pos, inventorystring);
}
void ItemSAO::step(float dtime, bool send_recommended)
{
assert(m_env);
const float interval = 0.2;
if(m_move_interval.step(dtime, interval)==false)
return;
dtime = interval;
core::aabbox3d<f32> box(-BS/3.,0.0,-BS/3., BS/3.,BS*2./3.,BS/3.);
collisionMoveResult moveresult;
// Apply gravity
m_speed_f += v3f(0, -dtime*9.81*BS, 0);
// Maximum movement without glitches
f32 pos_max_d = BS*0.25;
// Limit speed
if(m_speed_f.getLength()*dtime > pos_max_d)
m_speed_f *= pos_max_d / (m_speed_f.getLength()*dtime);
v3f pos_f = getBasePosition();
v3f pos_f_old = pos_f;
moveresult = collisionMoveSimple(&m_env->getMap(), pos_max_d,
box, dtime, pos_f, m_speed_f);
if(send_recommended == false)
return;
if(pos_f.getDistanceFrom(m_last_sent_position) > 0.05*BS)
{
setBasePosition(pos_f);
m_last_sent_position = pos_f;
std::ostringstream os(std::ios::binary);
char buf[6];
// command (0 = update position)
buf[0] = 0;
os.write(buf, 1);
// pos
writeS32((u8*)buf, m_base_position.X*1000);
os.write(buf, 4);
writeS32((u8*)buf, m_base_position.Y*1000);
os.write(buf, 4);
writeS32((u8*)buf, m_base_position.Z*1000);
os.write(buf, 4);
// create message and add to list
ActiveObjectMessage aom(getId(), false, os.str());
m_messages_out.push_back(aom);
}
}
std::string ItemSAO::getClientInitializationData()
{
std::ostringstream os(std::ios::binary);
char buf[6];
// version
buf[0] = 0;
os.write(buf, 1);
// pos
writeS32((u8*)buf, m_base_position.X*1000);
os.write(buf, 4);
writeS32((u8*)buf, m_base_position.Y*1000);
os.write(buf, 4);
writeS32((u8*)buf, m_base_position.Z*1000);
os.write(buf, 4);
// inventorystring
os<<serializeString(m_inventorystring);
return os.str();
}
std::string ItemSAO::getStaticData()
{
dstream<<__FUNCTION_NAME<<std::endl;
std::ostringstream os(std::ios::binary);
char buf[1];
// version
buf[0] = 0;
os.write(buf, 1);
// inventorystring
os<<serializeString(m_inventorystring);
return os.str();
}
InventoryItem * ItemSAO::createInventoryItem()
{
try{
std::istringstream is(m_inventorystring, std::ios_base::binary);
InventoryItem *item = InventoryItem::deSerialize(is);
dstream<<__FUNCTION_NAME<<": m_inventorystring=\""
<<m_inventorystring<<"\" -> item="<<item
<<std::endl;
return item;
}
catch(SerializationError &e)
{
dstream<<__FUNCTION_NAME<<": serialization error: "
<<"m_inventorystring=\""<<m_inventorystring<<"\""<<std::endl;
return NULL;
}
}
/*
RatSAO
*/
// Prototype
RatSAO proto_RatSAO(NULL, 0, v3f(0,0,0));
RatSAO::RatSAO(ServerEnvironment *env, u16 id, v3f pos):
ServerActiveObject(env, id, pos),
m_is_active(false),
m_speed_f(0,0,0)
{
ServerActiveObject::registerType(getType(), create);
m_oldpos = v3f(0,0,0);
m_last_sent_position = v3f(0,0,0);
m_yaw = 0;
m_counter1 = 0;
m_counter2 = 0;
m_age = 0;
m_touching_ground = false;
}
ServerActiveObject* RatSAO::create(ServerEnvironment *env, u16 id, v3f pos,
const std::string &data)
{
std::istringstream is(data, std::ios::binary);
char buf[1];
// read version
is.read(buf, 1);
u8 version = buf[0];
// check if version is supported
if(version != 0)
return NULL;
return new RatSAO(env, id, pos);
}
void RatSAO::step(float dtime, bool send_recommended)
{
assert(m_env);
if(m_is_active == false)
{
if(m_inactive_interval.step(dtime, 0.5)==false)
return;
}
/*
The AI
*/
/*m_age += dtime;
if(m_age > 60)
{
// Die
m_removed = true;
return;
}*/
// Apply gravity
m_speed_f.Y -= dtime*9.81*BS;
/*
Move around if some player is close
*/
bool player_is_close = false;
// Check connected players
core::list<Player*> players = m_env->getPlayers(true);
core::list<Player*>::Iterator i;
for(i = players.begin();
i != players.end(); i++)
{
Player *player = *i;
v3f playerpos = player->getPosition();
if(m_base_position.getDistanceFrom(playerpos) < BS*10.0)
{
player_is_close = true;
break;
}
}
m_is_active = player_is_close;
if(player_is_close == false)
{
m_speed_f.X = 0;
m_speed_f.Z = 0;
}
else
{
// Move around
v3f dir(cos(m_yaw/180*PI),0,sin(m_yaw/180*PI));
f32 speed = 2*BS;
m_speed_f.X = speed * dir.X;
m_speed_f.Z = speed * dir.Z;
if(m_touching_ground && (m_oldpos - m_base_position).getLength()
< dtime*speed/2)
{
m_counter1 -= dtime;
if(m_counter1 < 0.0)
{
m_counter1 += 1.0;
m_speed_f.Y = 5.0*BS;
}
}
{
m_counter2 -= dtime;
if(m_counter2 < 0.0)
{
m_counter2 += (float)(myrand()%100)/100*3.0;
m_yaw += ((float)(myrand()%200)-100)/100*180;
m_yaw = wrapDegrees(m_yaw);
}
}
}
m_oldpos = m_base_position;
/*
Move it, with collision detection
*/
core::aabbox3d<f32> box(-BS/3.,0.0,-BS/3., BS/3.,BS*2./3.,BS/3.);
collisionMoveResult moveresult;
// Maximum movement without glitches
f32 pos_max_d = BS*0.25;
// Limit speed
if(m_speed_f.getLength()*dtime > pos_max_d)
m_speed_f *= pos_max_d / (m_speed_f.getLength()*dtime);
v3f pos_f = getBasePosition();
v3f pos_f_old = pos_f;
moveresult = collisionMoveSimple(&m_env->getMap(), pos_max_d,
box, dtime, pos_f, m_speed_f);
m_touching_ground = moveresult.touching_ground;
setBasePosition(pos_f);
if(send_recommended == false)
return;
if(pos_f.getDistanceFrom(m_last_sent_position) > 0.05*BS)
{
m_last_sent_position = pos_f;
std::ostringstream os(std::ios::binary);
// command (0 = update position)
writeU8(os, 0);
// pos
writeV3F1000(os, m_base_position);
// yaw
writeF1000(os, m_yaw);
// create message and add to list
ActiveObjectMessage aom(getId(), false, os.str());
m_messages_out.push_back(aom);
}
}
std::string RatSAO::getClientInitializationData()
{
std::ostringstream os(std::ios::binary);
// version
writeU8(os, 0);
// pos
writeV3F1000(os, m_base_position);
return os.str();
}
std::string RatSAO::getStaticData()
{
//dstream<<__FUNCTION_NAME<<std::endl;
std::ostringstream os(std::ios::binary);
// version
writeU8(os, 0);
return os.str();
}
InventoryItem* RatSAO::createPickedUpItem()
{
std::istringstream is("CraftItem rat 1", std::ios_base::binary);
InventoryItem *item = InventoryItem::deSerialize(is);
return item;
}
/*
Oerkki1SAO
*/
// Y is copied, X and Z change is limited
void accelerate_xz(v3f &speed, v3f target_speed, f32 max_increase)
{
v3f d_wanted = target_speed - speed;
d_wanted.Y = 0;
f32 dl_wanted = d_wanted.getLength();
f32 dl = dl_wanted;
if(dl > max_increase)
dl = max_increase;
v3f d = d_wanted.normalize() * dl;
speed.X += d.X;
speed.Z += d.Z;
speed.Y = target_speed.Y;
}
// Prototype
Oerkki1SAO proto_Oerkki1SAO(NULL, 0, v3f(0,0,0));
Oerkki1SAO::Oerkki1SAO(ServerEnvironment *env, u16 id, v3f pos):
ServerActiveObject(env, id, pos),
m_is_active(false),
m_speed_f(0,0,0)
{
ServerActiveObject::registerType(getType(), create);
m_oldpos = v3f(0,0,0);
m_last_sent_position = v3f(0,0,0);
m_yaw = 0;
m_counter1 = 0;
m_counter2 = 0;
m_age = 0;
m_touching_ground = false;
m_hp = 20;
m_after_jump_timer = 0;
}
ServerActiveObject* Oerkki1SAO::create(ServerEnvironment *env, u16 id, v3f pos,
const std::string &data)
{
std::istringstream is(data, std::ios::binary);
// read version
u8 version = readU8(is);
// read hp
u8 hp = readU8(is);
// check if version is supported
if(version != 0)
return NULL;
Oerkki1SAO *o = new Oerkki1SAO(env, id, pos);
o->m_hp = hp;
return o;
}
void Oerkki1SAO::step(float dtime, bool send_recommended)
{
assert(m_env);
if(m_is_active == false)
{
if(m_inactive_interval.step(dtime, 0.5)==false)
return;
}
/*
The AI
*/
m_age += dtime;
if(m_age > 120)
{
// Die
m_removed = true;
return;
}
m_after_jump_timer -= dtime;
v3f old_speed = m_speed_f;
// Apply gravity
m_speed_f.Y -= dtime*9.81*BS;
/*
Move around if some player is close
*/
bool player_is_close = false;
bool player_is_too_close = false;
v3f near_player_pos;
// Check connected players
core::list<Player*> players = m_env->getPlayers(true);
core::list<Player*>::Iterator i;
for(i = players.begin();
i != players.end(); i++)
{
Player *player = *i;
v3f playerpos = player->getPosition();
f32 dist = m_base_position.getDistanceFrom(playerpos);
if(dist < BS*1.45)
{
player_is_too_close = true;
near_player_pos = playerpos;
break;
}
else if(dist < BS*15.0)
{
player_is_close = true;
near_player_pos = playerpos;
}
}
m_is_active = player_is_close;
v3f target_speed = m_speed_f;
if(!player_is_close)
{
target_speed = v3f(0,0,0);
}
else
{
// Move around
v3f ndir = near_player_pos - m_base_position;
ndir.Y = 0;
ndir.normalize();
f32 nyaw = 180./PI*atan2(ndir.Z,ndir.X);
if(nyaw < m_yaw - 180)
nyaw += 360;
else if(nyaw > m_yaw + 180)
nyaw -= 360;
m_yaw = 0.95*m_yaw + 0.05*nyaw;
m_yaw = wrapDegrees(m_yaw);
f32 speed = 2*BS;
if((m_touching_ground || m_after_jump_timer > 0.0)
&& !player_is_too_close)
{
v3f dir(cos(m_yaw/180*PI),0,sin(m_yaw/180*PI));
target_speed.X = speed * dir.X;
target_speed.Z = speed * dir.Z;
}
if(m_touching_ground && (m_oldpos - m_base_position).getLength()
< dtime*speed/2)
{
m_counter1 -= dtime;
if(m_counter1 < 0.0)
{
m_counter1 += 0.2;
// Jump
target_speed.Y = 5.0*BS;
m_after_jump_timer = 1.0;
}
}
{
m_counter2 -= dtime;
if(m_counter2 < 0.0)
{
m_counter2 += (float)(myrand()%100)/100*3.0;
//m_yaw += ((float)(myrand()%200)-100)/100*180;
m_yaw += ((float)(myrand()%200)-100)/100*90;
m_yaw = wrapDegrees(m_yaw);
}
}
}
if((m_speed_f - target_speed).getLength() > BS*4 || player_is_too_close)
accelerate_xz(m_speed_f, target_speed, dtime*BS*8);
else
accelerate_xz(m_speed_f, target_speed, dtime*BS*4);
m_oldpos = m_base_position;
/*
Move it, with collision detection
*/
core::aabbox3d<f32> box(-BS/3.,0.0,-BS/3., BS/3.,BS*5./3.,BS/3.);
collisionMoveResult moveresult;
// Maximum movement without glitches
f32 pos_max_d = BS*0.25;
/*// Limit speed
if(m_speed_f.getLength()*dtime > pos_max_d)
m_speed_f *= pos_max_d / (m_speed_f.getLength()*dtime);*/
v3f pos_f = getBasePosition();
v3f pos_f_old = pos_f;
moveresult = collisionMovePrecise(&m_env->getMap(), pos_max_d,
box, dtime, pos_f, m_speed_f);
m_touching_ground = moveresult.touching_ground;
// Do collision damage
float tolerance = BS*12;
float factor = BS*0.5;
v3f speed_diff = old_speed - m_speed_f;
// Increase effect in X and Z
speed_diff.X *= 2;
speed_diff.Z *= 2;
float vel = speed_diff.getLength();
if(vel > tolerance)
{
f32 damage_f = (vel - tolerance)/BS*factor;
u16 damage = (u16)(damage_f+0.5);
doDamage(damage);
}
setBasePosition(pos_f);
if(send_recommended == false && m_speed_f.getLength() < 3.0*BS)
return;
if(pos_f.getDistanceFrom(m_last_sent_position) > 0.05*BS)
{
m_last_sent_position = pos_f;
std::ostringstream os(std::ios::binary);
// command (0 = update position)
writeU8(os, 0);
// pos
writeV3F1000(os, m_base_position);
// yaw
writeF1000(os, m_yaw);
// create message and add to list
ActiveObjectMessage aom(getId(), false, os.str());
m_messages_out.push_back(aom);
}
}
std::string Oerkki1SAO::getClientInitializationData()
{
std::ostringstream os(std::ios::binary);
// version
writeU8(os, 0);
// pos
writeV3F1000(os, m_base_position);
return os.str();
}
std::string Oerkki1SAO::getStaticData()
{
//dstream<<__FUNCTION_NAME<<std::endl;
std::ostringstream os(std::ios::binary);
// version
writeU8(os, 0);
// hp
writeU8(os, m_hp);
return os.str();
}
u16 Oerkki1SAO::punch(const std::string &toolname, v3f dir)
{
m_speed_f += dir*12*BS;
u16 amount = 5;
doDamage(amount);
return 65536/100;
}
void Oerkki1SAO::doDamage(u16 d)
{
dstream<<"oerkki damage: "<<d<<std::endl;
if(d < m_hp)
{
m_hp -= d;
}
else
{
// Die
m_hp = 0;
m_removed = true;
}
{
std::ostringstream os(std::ios::binary);
// command (1 = damage)
writeU8(os, 1);
// amount
writeU8(os, d);
// create message and add to list
ActiveObjectMessage aom(getId(), false, os.str());
m_messages_out.push_back(aom);
}
}

118
src/content_sao.h Normal file

@ -0,0 +1,118 @@
/*
Minetest-c55
Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef CONTENT_SAO_HEADER
#define CONTENT_SAO_HEADER
#include "serverobject.h"
#include "content_object.h"
class TestSAO : public ServerActiveObject
{
public:
TestSAO(ServerEnvironment *env, u16 id, v3f pos);
u8 getType() const
{return ACTIVEOBJECT_TYPE_TEST;}
static ServerActiveObject* create(ServerEnvironment *env, u16 id, v3f pos,
const std::string &data);
void step(float dtime, bool send_recommended);
private:
float m_timer1;
float m_age;
};
class ItemSAO : public ServerActiveObject
{
public:
ItemSAO(ServerEnvironment *env, u16 id, v3f pos,
const std::string inventorystring);
u8 getType() const
{return ACTIVEOBJECT_TYPE_ITEM;}
static ServerActiveObject* create(ServerEnvironment *env, u16 id, v3f pos,
const std::string &data);
void step(float dtime, bool send_recommended);
std::string getClientInitializationData();
std::string getStaticData();
InventoryItem* createInventoryItem();
InventoryItem* createPickedUpItem(){return createInventoryItem();}
private:
std::string m_inventorystring;
v3f m_speed_f;
v3f m_last_sent_position;
IntervalLimiter m_move_interval;
};
class RatSAO : public ServerActiveObject
{
public:
RatSAO(ServerEnvironment *env, u16 id, v3f pos);
u8 getType() const
{return ACTIVEOBJECT_TYPE_RAT;}
static ServerActiveObject* create(ServerEnvironment *env, u16 id, v3f pos,
const std::string &data);
void step(float dtime, bool send_recommended);
std::string getClientInitializationData();
std::string getStaticData();
InventoryItem* createPickedUpItem();
private:
bool m_is_active;
IntervalLimiter m_inactive_interval;
v3f m_speed_f;
v3f m_oldpos;
v3f m_last_sent_position;
float m_yaw;
float m_counter1;
float m_counter2;
float m_age;
bool m_touching_ground;
};
class Oerkki1SAO : public ServerActiveObject
{
public:
Oerkki1SAO(ServerEnvironment *env, u16 id, v3f pos);
u8 getType() const
{return ACTIVEOBJECT_TYPE_OERKKI1;}
static ServerActiveObject* create(ServerEnvironment *env, u16 id, v3f pos,
const std::string &data);
void step(float dtime, bool send_recommended);
std::string getClientInitializationData();
std::string getStaticData();
InventoryItem* createPickedUpItem(){return NULL;}
u16 punch(const std::string &toolname, v3f dir);
private:
void doDamage(u16 d);
bool m_is_active;
IntervalLimiter m_inactive_interval;
v3f m_speed_f;
v3f m_oldpos;
v3f m_last_sent_position;
float m_yaw;
float m_counter1;
float m_counter2;
float m_age;
bool m_touching_ground;
u8 m_hp;
float m_after_jump_timer;
};
#endif

@ -42,6 +42,8 @@ void set_default_settings()
g_settings.setDefault("keymap_rangeselect", "KEY_KEY_R"); g_settings.setDefault("keymap_rangeselect", "KEY_KEY_R");
g_settings.setDefault("keymap_freemove", "KEY_KEY_K"); g_settings.setDefault("keymap_freemove", "KEY_KEY_K");
g_settings.setDefault("keymap_fastmove", "KEY_KEY_J"); g_settings.setDefault("keymap_fastmove", "KEY_KEY_J");
g_settings.setDefault("keymap_frametime_graph", "KEY_F1");
g_settings.setDefault("keymap_screenshot", "KEY_F12");
// Some (temporary) keys for debugging // Some (temporary) keys for debugging
g_settings.setDefault("keymap_special1", "KEY_KEY_E"); g_settings.setDefault("keymap_special1", "KEY_KEY_E");
g_settings.setDefault("keymap_print_debug_stacks", "KEY_KEY_P"); g_settings.setDefault("keymap_print_debug_stacks", "KEY_KEY_P");
@ -54,7 +56,7 @@ void set_default_settings()
g_settings.setDefault("screenH", "600"); g_settings.setDefault("screenH", "600");
g_settings.setDefault("address", ""); g_settings.setDefault("address", "");
g_settings.setDefault("random_input", "false"); g_settings.setDefault("random_input", "false");
g_settings.setDefault("client_delete_unused_sectors_timeout", "1200"); g_settings.setDefault("client_unload_unused_data_timeout", "600");
g_settings.setDefault("enable_fog", "true"); g_settings.setDefault("enable_fog", "true");
g_settings.setDefault("new_style_water", "false"); g_settings.setDefault("new_style_water", "false");
g_settings.setDefault("new_style_leaves", "true"); g_settings.setDefault("new_style_leaves", "true");
@ -72,6 +74,7 @@ void set_default_settings()
g_settings.setDefault("farmesh_distance", "40"); g_settings.setDefault("farmesh_distance", "40");
g_settings.setDefault("enable_clouds", "true"); g_settings.setDefault("enable_clouds", "true");
g_settings.setDefault("invisible_stone", "false"); g_settings.setDefault("invisible_stone", "false");
g_settings.setDefault("screenshot_path", ".");
// Server stuff // Server stuff
g_settings.setDefault("enable_experimental", "false"); g_settings.setDefault("enable_experimental", "false");
@ -81,17 +84,19 @@ void set_default_settings()
g_settings.setDefault("default_password", ""); g_settings.setDefault("default_password", "");
g_settings.setDefault("default_privs", "build, shout"); g_settings.setDefault("default_privs", "build, shout");
g_settings.setDefault("profiler_print_interval", "0"); g_settings.setDefault("profiler_print_interval", "0");
g_settings.setDefault("enable_mapgen_debug_info", "false");
g_settings.setDefault("objectdata_interval", "0.2"); g_settings.setDefault("objectdata_interval", "0.2");
g_settings.setDefault("active_object_range", "2"); g_settings.setDefault("active_object_range", "2");
//g_settings.setDefault("max_simultaneous_block_sends_per_client", "1"); //g_settings.setDefault("max_simultaneous_block_sends_per_client", "1");
// This causes frametime jitter on client side, or does it?
g_settings.setDefault("max_simultaneous_block_sends_per_client", "2"); g_settings.setDefault("max_simultaneous_block_sends_per_client", "2");
g_settings.setDefault("max_simultaneous_block_sends_server_total", "8"); g_settings.setDefault("max_simultaneous_block_sends_server_total", "8");
g_settings.setDefault("max_block_send_distance", "8"); g_settings.setDefault("max_block_send_distance", "8");
g_settings.setDefault("max_block_generate_distance", "8"); g_settings.setDefault("max_block_generate_distance", "8");
g_settings.setDefault("time_send_interval", "20"); g_settings.setDefault("time_send_interval", "20");
g_settings.setDefault("time_speed", "96"); g_settings.setDefault("time_speed", "96");
g_settings.setDefault("server_unload_unused_sectors_timeout", "60"); g_settings.setDefault("server_unload_unused_data_timeout", "60");
g_settings.setDefault("server_map_save_interval", "60"); g_settings.setDefault("server_map_save_interval", "60");
g_settings.setDefault("full_block_send_enable_min_time_from_building", "2.0"); g_settings.setDefault("full_block_send_enable_min_time_from_building", "2.0");
//g_settings.setDefault("dungeon_rarity", "0.025"); //g_settings.setDefault("dungeon_rarity", "0.025");

@ -22,7 +22,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "porting.h" #include "porting.h"
#include "collision.h" #include "collision.h"
#include "content_mapnode.h" #include "content_mapnode.h"
#include "mapblock.h"
#include "serverobject.h"
#include "content_sao.h"
Environment::Environment(): Environment::Environment():
m_time_of_day(9000) m_time_of_day(9000)
@ -658,14 +660,6 @@ void ServerEnvironment::step(float dtime)
m_game_time_fraction_counter -= (float)inc_i; m_game_time_fraction_counter -= (float)inc_i;
} }
/*
Let map update it's timers
*/
{
//TimeTaker timer("Server m_map->timerUpdate()");
m_map->timerUpdate(dtime);
}
/* /*
Handle players Handle players
*/ */
@ -867,6 +861,9 @@ void ServerEnvironment::step(float dtime)
MapBlock *block = m_map->getBlockNoCreateNoEx(p); MapBlock *block = m_map->getBlockNoCreateNoEx(p);
if(block==NULL) if(block==NULL)
continue; continue;
// Reset block usage timer
block->resetUsageTimer();
// Set current time as timestamp // Set current time as timestamp
block->setTimestampNoChangedFlag(m_game_time); block->setTimestampNoChangedFlag(m_game_time);
@ -986,8 +983,14 @@ void ServerEnvironment::step(float dtime)
// Don't step if is to be removed or stored statically // Don't step if is to be removed or stored statically
if(obj->m_removed || obj->m_pending_deactivation) if(obj->m_removed || obj->m_pending_deactivation)
continue; continue;
// Step object, putting messages directly to the queue // Step object
obj->step(dtime, m_active_object_messages, send_recommended); obj->step(dtime, send_recommended);
// Read messages from object
while(obj->m_messages_out.size() > 0)
{
m_active_object_messages.push_back(
obj->m_messages_out.pop_front());
}
} }
} }
@ -1242,7 +1245,7 @@ u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
block->setChangedFlag(); block->setChangedFlag();
} }
else{ else{
dstream<<"WARNING: Server: Could not find a block for " dstream<<"WARNING: ServerEnv: Could not find a block for "
<<"storing newly added static active object"<<std::endl; <<"storing newly added static active object"<<std::endl;
} }
@ -1414,7 +1417,20 @@ void ServerEnvironment::deactivateFarObjects(bool force_delete)
StaticObject s_obj(obj->getType(), objectpos, staticdata); StaticObject s_obj(obj->getType(), objectpos, staticdata);
// Add to the block where the object is located in // Add to the block where the object is located in
v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS)); v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos); // Get or generate the block
MapBlock *block = m_map->emergeBlock(blockpos);
/*MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
if(block == NULL)
{
// Block not found. Is the old block still ok?
if(oldblock)
block = oldblock;
// Load from disk or generate
else
block = m_map->emergeBlock(blockpos);
}*/
if(block) if(block)
{ {
block->m_static_objects.insert(0, s_obj); block->m_static_objects.insert(0, s_obj);
@ -1422,17 +1438,9 @@ void ServerEnvironment::deactivateFarObjects(bool force_delete)
obj->m_static_exists = true; obj->m_static_exists = true;
obj->m_static_block = block->getPos(); obj->m_static_block = block->getPos();
} }
// If not possible, add back to previous block
else if(oldblock)
{
oldblock->m_static_objects.insert(0, s_obj);
oldblock->setChangedFlag();
obj->m_static_exists = true;
obj->m_static_block = oldblock->getPos();
}
else{ else{
dstream<<"WARNING: Server: Could not find a block for " dstream<<"WARNING: ServerEnv: Could not find or generate "
<<"storing static object"<<std::endl; <<"a block for storing static object"<<std::endl;
obj->m_static_exists = false; obj->m_static_exists = false;
continue; continue;
} }
@ -1526,11 +1534,6 @@ void ClientEnvironment::step(float dtime)
bool free_move = g_settings.getBool("free_move"); bool free_move = g_settings.getBool("free_move");
bool footprints = g_settings.getBool("footprints"); bool footprints = g_settings.getBool("footprints");
{
//TimeTaker timer("Client m_map->timerUpdate()");
m_map->timerUpdate(dtime);
}
// Get local player // Get local player
LocalPlayer *lplayer = getLocalPlayer(); LocalPlayer *lplayer = getLocalPlayer();
assert(lplayer); assert(lplayer);
@ -1728,17 +1731,21 @@ void ClientEnvironment::step(float dtime)
ClientActiveObject* obj = i.getNode()->getValue(); ClientActiveObject* obj = i.getNode()->getValue();
// Step object // Step object
obj->step(dtime, this); obj->step(dtime, this);
// Update lighting
//u8 light = LIGHT_MAX; if(m_active_object_light_update_interval.step(dtime, 0.21))
u8 light = 0; {
try{ // Update lighting
// Get node at head //u8 light = LIGHT_MAX;
v3s16 p = obj->getLightPosition(); u8 light = 0;
MapNode n = m_map->getNode(p); try{
light = n.getLightBlend(getDayNightRatio()); // Get node at head
v3s16 p = obj->getLightPosition();
MapNode n = m_map->getNode(p);
light = n.getLightBlend(getDayNightRatio());
}
catch(InvalidPositionException &e) {}
obj->updateLight(light);
} }
catch(InvalidPositionException &e) {}
obj->updateLight(light);
} }
} }
@ -1926,6 +1933,22 @@ ClientEnvEvent ClientEnvironment::getClientEvent()
return m_client_event_queue.pop_front(); return m_client_event_queue.pop_front();
} }
void ClientEnvironment::drawPostFx(video::IVideoDriver* driver, v3f camera_pos)
{
/*LocalPlayer *player = getLocalPlayer();
assert(player);
v3f pos_f = player->getPosition() + v3f(0,BS*1.625,0);*/
v3f pos_f = camera_pos;
v3s16 p_nodes = floatToInt(pos_f, BS);
MapNode n = m_map->getNodeNoEx(p_nodes);
if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
{
v2u32 ss = driver->getScreenSize();
core::rect<s32> rect(0,0, ss.X, ss.Y);
driver->draw2DRectangle(video::SColor(64, 100, 100, 200), rect);
}
}
#endif // #ifndef SERVER #endif // #ifndef SERVER

@ -36,6 +36,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "map.h" #include "map.h"
#include <ostream> #include <ostream>
#include "utility.h" #include "utility.h"
#include "activeobject.h"
class Server;
class ActiveBlockModifier;
class ServerActiveObject;
class Environment class Environment
{ {
@ -118,11 +123,6 @@ private:
This is not thread-safe. Server uses an environment mutex. This is not thread-safe. Server uses an environment mutex.
*/ */
#include "serverobject.h"
class Server;
class ActiveBlockModifier;
class ServerEnvironment : public Environment class ServerEnvironment : public Environment
{ {
public: public:
@ -406,12 +406,16 @@ public:
// Get event from queue. CEE_NONE is returned if queue is empty. // Get event from queue. CEE_NONE is returned if queue is empty.
ClientEnvEvent getClientEvent(); ClientEnvEvent getClientEvent();
// Post effects
void drawPostFx(video::IVideoDriver* driver, v3f camera_pos);
private: private:
ClientMap *m_map; ClientMap *m_map;
scene::ISceneManager *m_smgr; scene::ISceneManager *m_smgr;
core::map<u16, ClientActiveObject*> m_active_objects; core::map<u16, ClientActiveObject*> m_active_objects;
Queue<ClientEnvEvent> m_client_event_queue; Queue<ClientEnvEvent> m_client_event_queue;
IntervalLimiter m_active_object_light_update_interval;
}; };
#endif #endif

@ -280,7 +280,8 @@ void FarMesh::render()
if(h_avg < WATER_LEVEL*BS && h_max < (WATER_LEVEL+5)*BS) if(h_avg < WATER_LEVEL*BS && h_max < (WATER_LEVEL+5)*BS)
{ {
//c = video::SColor(255,59,86,146); //c = video::SColor(255,59,86,146);
c = video::SColor(255,82,120,204); //c = video::SColor(255,82,120,204);
c = video::SColor(255,74,105,170);
/*// Set to water level /*// Set to water level
for(u32 i=0; i<4; i++) for(u32 i=0; i<4; i++)

@ -30,8 +30,12 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "clouds.h" #include "clouds.h"
#include "keycode.h" #include "keycode.h"
#include "farmesh.h" #include "farmesh.h"
#include "mapblock.h"
// TODO: Move content-aware stuff to separate file /*
TODO: Move content-aware stuff to separate file by adding properties
and virtual interfaces
*/
#include "content_mapnode.h" #include "content_mapnode.h"
#include "content_nodemeta.h" #include "content_nodemeta.h"
@ -672,6 +676,34 @@ void update_skybox(video::IVideoDriver* driver,
} }
} }
/*
Draws a screen with a single text on it.
Text will be removed when the screen is drawn the next time.
*/
/*gui::IGUIStaticText **/
void draw_load_screen(const std::wstring &text,
video::IVideoDriver* driver, gui::IGUIFont* font)
{
v2u32 screensize = driver->getScreenSize();
const wchar_t *loadingtext = text.c_str();
core::vector2d<u32> textsize_u = font->getDimension(loadingtext);
core::vector2d<s32> textsize(textsize_u.X,textsize_u.Y);
core::vector2d<s32> center(screensize.X/2, screensize.Y/2);
core::rect<s32> textrect(center - textsize/2, center + textsize/2);
gui::IGUIStaticText *guitext = guienv->addStaticText(
loadingtext, textrect, false, false);
guitext->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
driver->beginScene(true, true, video::SColor(255,0,0,0));
guienv->drawAll();
driver->endScene();
guitext->remove();
//return guitext;
}
void the_game( void the_game(
bool &kill, bool &kill,
bool random_input, bool random_input,
@ -688,13 +720,18 @@ void the_game(
{ {
video::IVideoDriver* driver = device->getVideoDriver(); video::IVideoDriver* driver = device->getVideoDriver();
scene::ISceneManager* smgr = device->getSceneManager(); scene::ISceneManager* smgr = device->getSceneManager();
// Calculate text height using the font
u32 text_height = font->getDimension(L"Random test string").Height;
v2u32 screensize(0,0); v2u32 screensize(0,0);
v2u32 last_screensize(0,0); v2u32 last_screensize(0,0);
screensize = driver->getScreenSize(); screensize = driver->getScreenSize();
const s32 hotbar_itemcount = 8; const s32 hotbar_itemcount = 8;
const s32 hotbar_imagesize = 36; //const s32 hotbar_imagesize = 36;
//const s32 hotbar_imagesize = 64;
s32 hotbar_imagesize = 48;
// The color of the sky // The color of the sky
@ -705,20 +742,10 @@ void the_game(
/* /*
Draw "Loading" screen Draw "Loading" screen
*/ */
const wchar_t *loadingtext = L"Loading and connecting..."; /*gui::IGUIStaticText *gui_loadingtext = */
u32 text_height = font->getDimension(loadingtext).Height; //draw_load_screen(L"Loading and connecting...", driver, font);
core::vector2d<s32> center(screensize.X/2, screensize.Y/2);
core::vector2d<s32> textsize(300, text_height);
core::rect<s32> textrect(center - textsize/2, center + textsize/2);
gui::IGUIStaticText *gui_loadingtext = guienv->addStaticText(
loadingtext, textrect, false, false);
gui_loadingtext->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
driver->beginScene(true, true, video::SColor(255,0,0,0));
guienv->drawAll();
driver->endScene();
draw_load_screen(L"Loading...", driver, font);
/* /*
Create server. Create server.
@ -726,6 +753,7 @@ void the_game(
*/ */
SharedPtr<Server> server; SharedPtr<Server> server;
if(address == ""){ if(address == ""){
draw_load_screen(L"Creating server...", driver, font);
std::cout<<DTIME<<"Creating server"<<std::endl; std::cout<<DTIME<<"Creating server"<<std::endl;
server = new Server(map_dir); server = new Server(map_dir);
server->start(port); server->start(port);
@ -735,9 +763,11 @@ void the_game(
Create client Create client
*/ */
draw_load_screen(L"Creating client...", driver, font);
std::cout<<DTIME<<"Creating client"<<std::endl; std::cout<<DTIME<<"Creating client"<<std::endl;
Client client(device, playername.c_str(), password, draw_control); Client client(device, playername.c_str(), password, draw_control);
draw_load_screen(L"Resolving address...", driver, font);
Address connect_address(0,0,0,0, port); Address connect_address(0,0,0,0, port);
try{ try{
if(address == "") if(address == "")
@ -751,7 +781,7 @@ void the_game(
std::cout<<DTIME<<"Couldn't resolve address"<<std::endl; std::cout<<DTIME<<"Couldn't resolve address"<<std::endl;
//return 0; //return 0;
error_message = L"Couldn't resolve address"; error_message = L"Couldn't resolve address";
gui_loadingtext->remove(); //gui_loadingtext->remove();
return; return;
} }
@ -784,11 +814,17 @@ void the_game(
{ {
break; break;
} }
std::wostringstream ss;
ss<<L"Connecting to server... (timeout in ";
ss<<(int)(10.0 - time_counter + 1.0);
ss<<L" seconds)";
draw_load_screen(ss.str(), driver, font);
// Update screen /*// Update screen
driver->beginScene(true, true, video::SColor(255,0,0,0)); driver->beginScene(true, true, video::SColor(255,0,0,0));
guienv->drawAll(); guienv->drawAll();
driver->endScene(); driver->endScene();*/
// Update client and server // Update client and server
@ -818,7 +854,7 @@ void the_game(
error_message = L"Connection timed out."; error_message = L"Connection timed out.";
std::cout<<DTIME<<"Timed out."<<std::endl; std::cout<<DTIME<<"Timed out."<<std::endl;
} }
gui_loadingtext->remove(); //gui_loadingtext->remove();
return; return;
} }
@ -880,7 +916,7 @@ void the_game(
Move into game Move into game
*/ */
gui_loadingtext->remove(); //gui_loadingtext->remove();
/* /*
Add some gui stuff Add some gui stuff
@ -973,6 +1009,8 @@ void the_game(
while(device->run() && kill == false) while(device->run() && kill == false)
{ {
//std::cerr<<"frame"<<std::endl;
if(g_gamecallback->disconnect_requested) if(g_gamecallback->disconnect_requested)
{ {
g_gamecallback->disconnect_requested = false; g_gamecallback->disconnect_requested = false;
@ -998,6 +1036,14 @@ void the_game(
screensize = driver->getScreenSize(); screensize = driver->getScreenSize();
v2s32 displaycenter(screensize.X/2,screensize.Y/2); v2s32 displaycenter(screensize.X/2,screensize.Y/2);
//bool screensize_changed = screensize != last_screensize; //bool screensize_changed = screensize != last_screensize;
// Resize hotbar
if(screensize.Y <= 600)
hotbar_imagesize = 32;
else if(screensize.Y <= 1024)
hotbar_imagesize = 48;
else
hotbar_imagesize = 64;
// Hilight boxes collected during the loop and displayed // Hilight boxes collected during the loop and displayed
core::list< core::aabbox3d<f32> > hilightboxes; core::list< core::aabbox3d<f32> > hilightboxes;
@ -1090,7 +1136,7 @@ void the_game(
*/ */
static f32 dtime_avg1 = 0.0; static f32 dtime_avg1 = 0.0;
dtime_avg1 = dtime_avg1 * 0.98 + dtime * 0.02; dtime_avg1 = dtime_avg1 * 0.96 + dtime * 0.04;
f32 dtime_jitter1 = dtime - dtime_avg1; f32 dtime_jitter1 = dtime - dtime_avg1;
static f32 dtime_jitter1_max_sample = 0.0; static f32 dtime_jitter1_max_sample = 0.0;
@ -1254,6 +1300,38 @@ void the_game(
chat_lines.push_back(ChatLine(L"fast_move enabled")); chat_lines.push_back(ChatLine(L"fast_move enabled"));
} }
} }
else if(input->wasKeyDown(getKeySetting("keymap_frametime_graph")))
{
if(g_settings.getBool("frametime_graph"))
{
g_settings.set("frametime_graph","false");
chat_lines.push_back(ChatLine(L"frametime_graph disabled"));
}
else
{
g_settings.set("frametime_graph","true");
chat_lines.push_back(ChatLine(L"frametime_graph enabled"));
}
}
else if(input->wasKeyDown(getKeySetting("keymap_screenshot")))
{
irr::video::IImage* const image = driver->createScreenShot();
if (image) {
irr::c8 filename[256];
snprintf(filename, 256, "%s/screenshot_%u.png",
g_settings.get("screenshot_path").c_str(),
device->getTimer()->getRealTime());
if (driver->writeImageToFile(image, filename)) {
std::wstringstream sstr;
sstr<<"Saved screenshot to '"<<filename<<"'";
dstream<<"Saved screenshot to '"<<filename<<"'"<<std::endl;
chat_lines.push_back(ChatLine(sstr.str()));
} else{
dstream<<"Failed to save screenshot '"<<filename<<"'"<<std::endl;
}
image->drop();
}
}
// Item selection with mouse wheel // Item selection with mouse wheel
{ {
@ -2194,6 +2272,13 @@ void the_game(
core::rect<s32>(0,0,screensize.X,screensize.Y), core::rect<s32>(0,0,screensize.X,screensize.Y),
NULL); NULL);
} }
/*
Environment post fx
*/
{
client.getEnv()->drawPostFx(driver, camera_position);
}
/* /*
End scene End scene
@ -2237,15 +2322,12 @@ void the_game(
generator and other stuff quits generator and other stuff quits
*/ */
{ {
const wchar_t *shuttingdowntext = L"Shutting down stuff..."; /*gui::IGUIStaticText *gui_shuttingdowntext = */
gui::IGUIStaticText *gui_shuttingdowntext = guienv->addStaticText( draw_load_screen(L"Shutting down stuff...", driver, font);
shuttingdowntext, textrect, false, false); /*driver->beginScene(true, true, video::SColor(255,0,0,0));
gui_shuttingdowntext->setTextAlignment(gui::EGUIA_CENTER,
gui::EGUIA_UPPERLEFT);
driver->beginScene(true, true, video::SColor(255,0,0,0));
guienv->drawAll(); guienv->drawAll();
driver->endScene(); driver->endScene();
gui_shuttingdowntext->remove(); gui_shuttingdowntext->remove();*/
} }
} }

@ -30,6 +30,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "serverobject.h" #include "serverobject.h"
#include "content_mapnode.h" #include "content_mapnode.h"
#include "content_inventory.h" #include "content_inventory.h"
#include "content_sao.h"
/* /*
InventoryItem InventoryItem

@ -27,6 +27,33 @@ NOTE: Global locale is now set at initialization
NOTE: If VBO (EHM_STATIC) is used, remember to explicitly free the NOTE: If VBO (EHM_STATIC) is used, remember to explicitly free the
hardware buffer (it is not freed automatically) hardware buffer (it is not freed automatically)
NOTE: A random to-do list saved here as documentation:
A list of "active blocks" in which stuff happens. (+=done)
+ Add a never-resetted game timer to the server
+ Add a timestamp value to blocks
+ The simple rule: All blocks near some player are "active"
- Do stuff in real time in active blocks
+ Handle objects
- Grow grass, delete leaves without a tree
- Spawn some mobs based on some rules
- Transform cobble to mossy cobble near water
- Run a custom script
- ...And all kinds of other dynamic stuff
+ Keep track of when a block becomes active and becomes inactive
+ When a block goes inactive:
+ Store objects statically to block
+ Store timer value as the timestamp
+ When a block goes active:
+ Create active objects out of static objects
- Simulate the results of what would have happened if it would have
been active for all the time
- Grow a lot of grass and so on
+ Initially it is fine to send information about every active object
to every player. Eventually it should be modified to only send info
about the nearest ones.
+ This was left to be done by the old system and it sends only the
nearest ones.
Old, wild and random suggestions that probably won't be done: Old, wild and random suggestions that probably won't be done:
------------------------------------------------------------- -------------------------------------------------------------
@ -73,9 +100,6 @@ SUGG: Make the amount of blocks sending to client and the total
SUGG: Meshes of blocks could be split into 6 meshes facing into SUGG: Meshes of blocks could be split into 6 meshes facing into
different directions and then only those drawn that need to be different directions and then only those drawn that need to be
SUGG: Calculate lighting per vertex to get a lighting effect like in
bartwe's game
SUGG: Background music based on cellular automata? SUGG: Background music based on cellular automata?
http://www.earslap.com/projectslab/otomata http://www.earslap.com/projectslab/otomata
@ -90,6 +114,8 @@ SUGG: Make a system for pregenerating quick information for mapblocks, so
or even generated. or even generated.
SUGG: Erosion simulation at map generation time SUGG: Erosion simulation at map generation time
- This might be plausible if larger areas of map were pregenerated
without lighting (which is slow)
- Simulate water flows, which would carve out dirt fast and - Simulate water flows, which would carve out dirt fast and
then turn stone into gravel and sand and relocate it. then turn stone into gravel and sand and relocate it.
- How about relocating minerals, too? Coal and gold in - How about relocating minerals, too? Coal and gold in
@ -100,6 +126,16 @@ SUGG: Erosion simulation at map generation time
- Simulate rock falling from cliffs when water has removed - Simulate rock falling from cliffs when water has removed
enough solid rock from the bottom enough solid rock from the bottom
SUGG: For non-mapgen FarMesh: Add a per-sector database to store surface
stuff as simple flags/values
- Light?
- A building?
And at some point make the server send this data to the client too,
instead of referring to the noise functions
- Ground height
- Surface ground type
- Trees?
Gaming ideas: Gaming ideas:
------------- -------------
@ -173,12 +209,13 @@ SUGG: Make fetching sector's blocks more efficient when rendering
sectors that have very large amounts of blocks (on client) sectors that have very large amounts of blocks (on client)
- Is this necessary at all? - Is this necessary at all?
TODO: Flowing water animation
SUGG: Draw cubes in inventory directly with 3D drawing commands, so that SUGG: Draw cubes in inventory directly with 3D drawing commands, so that
animating them is easier. animating them is easier.
SUGG: Option for enabling proper alpha channel for textures SUGG: Option for enabling proper alpha channel for textures
TODO: Flowing water animation
TODO: A setting for enabling bilinear filtering for textures TODO: A setting for enabling bilinear filtering for textures
TODO: Better control of draw_control.wanted_max_blocks TODO: Better control of draw_control.wanted_max_blocks
@ -193,6 +230,12 @@ TODO: Artificial (night) light could be more yellow colored than sunlight.
SUGG: Somehow make the night less colorful SUGG: Somehow make the night less colorful
TODO: Occlusion culling
- At the same time, move some of the renderMap() block choosing code
to the same place as where the new culling happens.
- Shoot some rays per frame and when ready, make a new list of
blocks for usage of renderMap and give it a new pointer to it.
Configuration: Configuration:
-------------- --------------
@ -231,6 +274,7 @@ FIXME: Server sometimes goes into some infinite PeerNotFoundException loop
* Fix the problem with the server constantly saving one or a few * Fix the problem with the server constantly saving one or a few
blocks? List the first saved block, maybe it explains. blocks? List the first saved block, maybe it explains.
- It is probably caused by oscillating water - It is probably caused by oscillating water
- TODO: Investigate if this still happens (this is a very old one)
* Make a small history check to transformLiquids to detect and log * Make a small history check to transformLiquids to detect and log
continuous oscillations, in such detail that they can be fixed. continuous oscillations, in such detail that they can be fixed.
@ -238,44 +282,12 @@ FIXME: The new optimized map sending doesn't sometimes send enough blocks
from big caves and such from big caves and such
FIXME: Block send distance configuration does not take effect for some reason FIXME: Block send distance configuration does not take effect for some reason
TODO: Map saving should be done by EmergeThread
SUGG: Map unloading based on sector reference is not very good, it keeps
unnecessary stuff in memory. I guess. Investigate this.
TODO: When block is placed and it has param_type==CPT_FACEDIR_SIMPLE, set
the direction accordingly.
Environment: Environment:
------------ ------------
TODO: A list of "active blocks" in which stuff happens. (+=done) TODO: Add proper hooks to when adding and removing active blocks
+ Add a never-resetted game timer to the server
+ Add a timestamp value to blocks TODO: Finish the ActiveBlockModifier stuff and use it for something
+ The simple rule: All blocks near some player are "active"
- Do stuff in real time in active blocks
+ Handle objects
TODO: Make proper hooks in here
- Grow grass, delete leaves without a tree
- Spawn some mobs based on some rules
- Transform cobble to mossy cobble near water
- Run a custom script
- ...And all kinds of other dynamic stuff
+ Keep track of when a block becomes active and becomes inactive
+ When a block goes inactive:
+ Store objects statically to block
+ Store timer value as the timestamp
+ When a block goes active:
+ Create active objects out of static objects
TODO: Make proper hooks in here
- Simulate the results of what would have happened if it would have
been active for all the time
- Grow a lot of grass and so on
+ Initially it is fine to send information about every active object
to every player. Eventually it should be modified to only send info
about the nearest ones.
+ This was left to be done by the old system and it sends only the
nearest ones.
Objects: Objects:
-------- --------
@ -287,6 +299,7 @@ TODO: Get rid of MapBlockObjects and use only ActiveObjects
SUGG: MovingObject::move and Player::move are basically the same. SUGG: MovingObject::move and Player::move are basically the same.
combine them. combine them.
- NOTE: This is a bit tricky because player has the sneaking ability
- NOTE: Player::move is more up-to-date. - NOTE: Player::move is more up-to-date.
- NOTE: There is a simple move implementation now in collision.{h,cpp} - NOTE: There is a simple move implementation now in collision.{h,cpp}
- NOTE: MovingObject will be deleted (MapBlockObject) - NOTE: MovingObject will be deleted (MapBlockObject)
@ -305,59 +318,25 @@ TODO: Mineral and ground material properties
TODO: Flowing water to actually contain flow direction information TODO: Flowing water to actually contain flow direction information
- There is a space for this - it just has to be implemented. - There is a space for this - it just has to be implemented.
SUGG: Try out the notch way of generating maps, that is, make bunches TODO: Consider smoothening cave floors after generating them
of low-res 3d noise and interpolate linearly.
Mapgen v2 (the current one):
* Possibly add some kind of erosion and other stuff
* Better water generation (spread it to underwater caverns but don't
fill dungeons that don't touch big water masses)
* When generating a chunk and the neighboring chunk doesn't have mud
and stuff yet and the ground is fairly flat, the mud will flow to
the other chunk making nasty straight walls when the other chunk
is generated. Fix it. Maybe just a special case if the ground is
flat?
* Consider not updating this one and make a good mainly block-based
generator
SUGG: Make two "modified states", one that forces the block to be saved at
the next save event, and one that makes the block to be saved at exit
time.
TODO: Add a not_fully_generated flag to MapBlock, which would be set for
blocks that contain eg. trees from neighboring generations but haven't
been generated itself. This is required for the future generator.
Misc. stuff: Misc. stuff:
------------ ------------
- Make sure server handles removing grass when a block is placed (etc) TODO: Make sure server handles removing grass when a block is placed (etc)
- The client should not do it by itself - The client should not do it by itself
- Block cube placement around player's head - NOTE: I think nobody does it currently...
- Protocol version field TODO: Block cube placement around player's head
- Consider getting some textures from cisoun's texture pack TODO: Protocol version field
- Ask from Cisoun TODO: Think about using same bits for material for fences and doors, for
- Make sure the fence implementation and data format is good example
- Think about using same bits for material for fences and doors, for TODO: Move mineral to param2, increment map serialization version, add
example conversion
- Finish the ActiveBlockModifier stuff and use it for something
- Move mineral to param2, increment map serialization version, add conversion
TODO: Add a per-sector database to store surface stuff as simple flags/values
- Light?
- A building?
And at some point make the server send this data to the client too,
instead of referring to the noise functions
- Ground height
- Surface ground type
- Trees?
TODO: Restart irrlicht completely when coming back to main menu from game. TODO: Restart irrlicht completely when coming back to main menu from game.
- This gets rid of everything that is stored in irrlicht's caches. - This gets rid of everything that is stored in irrlicht's caches.
TODO: Merge bahamada's audio stuff (clean patch available) TODO: Merge bahamada's audio stuff (clean patch available)
TODO: Merge spongie's chest/furnace direction (by hand)
TODO: Merge key configuration menu (no clean patch available) TODO: Merge key configuration menu (no clean patch available)
Making it more portable: Making it more portable:
@ -375,9 +354,6 @@ Stuff to do after release:
Doing currently: Doing currently:
---------------- ----------------
TODO: Use MapBlock::resetUsageTimer() in appropriate places
(on client and server)
====================================================================== ======================================================================
*/ */
@ -406,16 +382,12 @@ TODO: Use MapBlock::resetUsageTimer() in appropriate places
#include <iostream> #include <iostream>
#include <fstream> #include <fstream>
//#include <jmutexautolock.h>
#include <locale.h> #include <locale.h>
#include "main.h" #include "main.h"
#include "common_irrlicht.h" #include "common_irrlicht.h"
#include "debug.h" #include "debug.h"
//#include "map.h"
//#include "player.h"
#include "test.h" #include "test.h"
#include "server.h" #include "server.h"
//#include "client.h"
#include "constants.h" #include "constants.h"
#include "porting.h" #include "porting.h"
#include "gettime.h" #include "gettime.h"
@ -424,11 +396,10 @@ TODO: Use MapBlock::resetUsageTimer() in appropriate places
#include "config.h" #include "config.h"
#include "guiMainMenu.h" #include "guiMainMenu.h"
#include "mineral.h" #include "mineral.h"
//#include "noise.h"
//#include "tile.h"
#include "materials.h" #include "materials.h"
#include "game.h" #include "game.h"
#include "keycode.h" #include "keycode.h"
#include "tile.h"
// This makes textures // This makes textures
ITextureSource *g_texturesource = NULL; ITextureSource *g_texturesource = NULL;

@ -25,7 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
extern Settings g_settings; extern Settings g_settings;
// This makes and maps textures // This makes and maps textures
#include "tile.h" class ITextureSource;
extern ITextureSource *g_texturesource; extern ITextureSource *g_texturesource;
// Global profiler // Global profiler

@ -18,18 +18,16 @@ with this program; if not, write to the Free Software Foundation, Inc.,
*/ */
#include "map.h" #include "map.h"
#include "mapsector.h"
#include "mapblock.h"
#include "main.h" #include "main.h"
#include "jmutexautolock.h"
#include "client.h" #include "client.h"
#include "filesys.h" #include "filesys.h"
#include "utility.h" #include "utility.h"
#include "voxel.h" #include "voxel.h"
#include "porting.h" #include "porting.h"
#include "mineral.h"
#include "noise.h"
#include "serverobject.h"
#include "content_mapnode.h"
#include "mapgen.h" #include "mapgen.h"
#include "nodemetadata.h"
extern "C" { extern "C" {
#include "sqlite3.h" #include "sqlite3.h"
@ -122,42 +120,23 @@ MapSector * Map::getSectorNoGenerate(v2s16 p)
return sector; return sector;
} }
MapBlock * Map::getBlockNoCreate(v3s16 p3d)
{
v2s16 p2d(p3d.X, p3d.Z);
MapSector * sector = getSectorNoGenerate(p2d);
MapBlock *block = sector->getBlockNoCreate(p3d.Y);
return block;
}
MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d) MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
{ {
try v2s16 p2d(p3d.X, p3d.Z);
{ MapSector * sector = getSectorNoGenerateNoEx(p2d);
v2s16 p2d(p3d.X, p3d.Z); if(sector == NULL)
MapSector * sector = getSectorNoGenerate(p2d);
MapBlock *block = sector->getBlockNoCreate(p3d.Y);
return block;
}
catch(InvalidPositionException &e)
{
return NULL; return NULL;
} MapBlock *block = sector->getBlockNoCreateNoEx(p3d.Y);
return block;
} }
/*MapBlock * Map::getBlockCreate(v3s16 p3d) MapBlock * Map::getBlockNoCreate(v3s16 p3d)
{ {
v2s16 p2d(p3d.X, p3d.Z); MapBlock *block = getBlockNoCreateNoEx(p3d);
MapSector * sector = getSectorCreate(p2d); if(block == NULL)
assert(sector); throw InvalidPositionException();
MapBlock *block = sector->getBlockNoCreate(p3d.Y);
if(block)
return block;
block = sector->createBlankBlock(p3d.Y);
return block; return block;
}*/ }
bool Map::isNodeUnderground(v3s16 p) bool Map::isNodeUnderground(v3s16 p)
{ {
@ -172,6 +151,45 @@ bool Map::isNodeUnderground(v3s16 p)
} }
} }
bool Map::isValidPosition(v3s16 p)
{
v3s16 blockpos = getNodeBlockPos(p);
MapBlock *block = getBlockNoCreate(blockpos);
return (block != NULL);
}
// Returns a CONTENT_IGNORE node if not found
MapNode Map::getNodeNoEx(v3s16 p)
{
v3s16 blockpos = getNodeBlockPos(p);
MapBlock *block = getBlockNoCreateNoEx(blockpos);
if(block == NULL)
return MapNode(CONTENT_IGNORE);
v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
return block->getNodeNoCheck(relpos);
}
// throws InvalidPositionException if not found
MapNode Map::getNode(v3s16 p)
{
v3s16 blockpos = getNodeBlockPos(p);
MapBlock *block = getBlockNoCreateNoEx(blockpos);
if(block == NULL)
throw InvalidPositionException();
v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
return block->getNodeNoCheck(relpos);
}
// throws InvalidPositionException if not found
void Map::setNode(v3s16 p, MapNode & n)
{
v3s16 blockpos = getNodeBlockPos(p);
MapBlock *block = getBlockNoCreate(blockpos);
v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
block->setNodeNoCheck(relpos, n);
}
/* /*
Goes recursively through the neighbours of the node. Goes recursively through the neighbours of the node.
@ -737,6 +755,25 @@ void Map::updateLighting(enum LightBank bank,
} }
} }
/*
Enable this to disable proper lighting for speeding up map
generation for testing or whatever
*/
#if 0
//if(g_settings.get(""))
{
core::map<v3s16, MapBlock*>::Iterator i;
i = blocks_to_update.getIterator();
for(; i.atEnd() == false; i++)
{
MapBlock *block = i.getNode()->getValue();
v3s16 p = block->getPos();
block->setLightingExpired(false);
}
return;
}
#endif
#if 0 #if 0
{ {
@ -879,7 +916,7 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n,
{ {
} }
#if 1 #if 0
/* /*
If the new node is solid and there is grass below, change it to mud If the new node is solid and there is grass below, change it to mud
*/ */
@ -1357,9 +1394,14 @@ bool Map::dayNightDiffed(v3s16 blockpos)
/* /*
Updates usage timers Updates usage timers
*/ */
void Map::timerUpdate(float dtime) void Map::timerUpdate(float dtime, float unload_timeout,
core::list<v3s16> *unloaded_blocks)
{ {
//JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out bool save_before_unloading = (mapType() == MAPTYPE_SERVER);
core::list<v2s16> sector_deletion_queue;
u32 deleted_blocks_count = 0;
u32 saved_blocks_count = 0;
core::map<v2s16, MapSector*>::Iterator si; core::map<v2s16, MapSector*>::Iterator si;
@ -1368,48 +1410,85 @@ void Map::timerUpdate(float dtime)
{ {
MapSector *sector = si.getNode()->getValue(); MapSector *sector = si.getNode()->getValue();
bool all_blocks_deleted = true;
core::list<MapBlock*> blocks; core::list<MapBlock*> blocks;
sector->getBlocks(blocks); sector->getBlocks(blocks);
for(core::list<MapBlock*>::Iterator i = blocks.begin(); for(core::list<MapBlock*>::Iterator i = blocks.begin();
i != blocks.end(); i++) i != blocks.end(); i++)
{ {
(*i)->incrementUsageTimer(dtime); MapBlock *block = (*i);
block->incrementUsageTimer(dtime);
if(block->getUsageTimer() > unload_timeout)
{
v3s16 p = block->getPos();
// Save if modified
if(block->getModified() != MOD_STATE_CLEAN
&& save_before_unloading)
{
saveBlock(block);
saved_blocks_count++;
}
// Delete from memory
sector->deleteBlock(block);
if(unloaded_blocks)
unloaded_blocks->push_back(p);
deleted_blocks_count++;
}
else
{
all_blocks_deleted = false;
}
} }
if(all_blocks_deleted)
{
sector_deletion_queue.push_back(si.getNode()->getKey());
}
}
// Finally delete the empty sectors
deleteSectors(sector_deletion_queue);
if(deleted_blocks_count != 0)
{
PrintInfo(dstream); // ServerMap/ClientMap:
dstream<<"Unloaded "<<deleted_blocks_count
<<" blocks from memory";
if(save_before_unloading)
dstream<<", of which "<<saved_blocks_count<<" were written";
dstream<<"."<<std::endl;
} }
} }
void Map::deleteSectors(core::list<v2s16> &list, bool only_blocks) void Map::deleteSectors(core::list<v2s16> &list)
{ {
core::list<v2s16>::Iterator j; core::list<v2s16>::Iterator j;
for(j=list.begin(); j!=list.end(); j++) for(j=list.begin(); j!=list.end(); j++)
{ {
MapSector *sector = m_sectors[*j]; MapSector *sector = m_sectors[*j];
if(only_blocks) // If sector is in sector cache, remove it from there
{ if(m_sector_cache == sector)
sector->deleteBlocks(); m_sector_cache = NULL;
} // Remove from map and delete
else m_sectors.remove(*j);
{ delete sector;
/*
If sector is in sector cache, remove it from there
*/
if(m_sector_cache == sector)
{
m_sector_cache = NULL;
}
/*
Remove from map and delete
*/
m_sectors.remove(*j);
delete sector;
}
} }
} }
u32 Map::unloadUnusedData(float timeout, bool only_blocks, #if 0
void Map::unloadUnusedData(float timeout,
core::list<v3s16> *deleted_blocks) core::list<v3s16> *deleted_blocks)
{ {
core::list<v2s16> sector_deletion_queue; core::list<v2s16> sector_deletion_queue;
u32 deleted_blocks_count = 0;
u32 saved_blocks_count = 0;
core::map<v2s16, MapSector*>::Iterator si = m_sectors.getIterator(); core::map<v2s16, MapSector*>::Iterator si = m_sectors.getIterator();
for(; si.atEnd() == false; si++) for(; si.atEnd() == false; si++)
@ -1424,15 +1503,18 @@ u32 Map::unloadUnusedData(float timeout, bool only_blocks,
i != blocks.end(); i++) i != blocks.end(); i++)
{ {
MapBlock *block = (*i); MapBlock *block = (*i);
if(block->getUsageTimer() > timeout) if(block->getUsageTimer() > timeout)
{ {
// Save if modified // Save if modified
if(block->getModified() != MOD_STATE_CLEAN) if(block->getModified() != MOD_STATE_CLEAN)
{
saveBlock(block); saveBlock(block);
// Unload saved_blocks_count++;
sector->removeBlock(block); }
delete block; // Delete from memory
sector->deleteBlock(block);
deleted_blocks_count++;
} }
else else
{ {
@ -1446,37 +1528,16 @@ u32 Map::unloadUnusedData(float timeout, bool only_blocks,
} }
} }
#if 0 deleteSectors(sector_deletion_queue);
core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
for(; i.atEnd() == false; i++)
{
MapSector *sector = i.getNode()->getValue();
/*
Delete sector from memory if it hasn't been used in a long time
*/
if(sector->usage_timer > timeout)
{
sector_deletion_queue.push_back(i.getNode()->getKey());
if(deleted_blocks != NULL) dstream<<"Map: Unloaded "<<deleted_blocks_count<<" blocks from memory"
{ <<", of which "<<saved_blocks_count<<" were wr."
// Collect positions of blocks of sector <<std::endl;
MapSector *sector = i.getNode()->getValue();
core::list<MapBlock*> blocks;
sector->getBlocks(blocks);
for(core::list<MapBlock*>::Iterator i = blocks.begin();
i != blocks.end(); i++)
{
deleted_blocks->push_back((*i)->getPos());
}
}
}
}
#endif
deleteSectors(sector_deletion_queue, only_blocks); //return sector_deletion_queue.getSize();
return sector_deletion_queue.getSize(); //return deleted_blocks_count;
} }
#endif
void Map::PrintInfo(std::ostream &out) void Map::PrintInfo(std::ostream &out)
{ {
@ -1503,7 +1564,7 @@ void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
*/ */
v3s16 p0 = m_transforming_liquid.pop_front(); v3s16 p0 = m_transforming_liquid.pop_front();
MapNode n0 = getNode(p0); MapNode n0 = getNodeNoEx(p0);
// Don't deal with non-liquids // Don't deal with non-liquids
if(content_liquid(n0.d) == false) if(content_liquid(n0.d) == false)
@ -1538,13 +1599,10 @@ void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
}; };
for(u16 i=0; i<5; i++) for(u16 i=0; i<5; i++)
{ {
try
{
bool from_top = (i==0); bool from_top = (i==0);
v3s16 p2 = p0 + dirs_from[i]; v3s16 p2 = p0 + dirs_from[i];
MapNode n2 = getNode(p2); MapNode n2 = getNodeNoEx(p2);
if(content_liquid(n2.d)) if(content_liquid(n2.d))
{ {
@ -1593,10 +1651,6 @@ void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
if(new_liquid_level > new_liquid_level_max) if(new_liquid_level > new_liquid_level_max)
new_liquid_level_max = new_liquid_level; new_liquid_level_max = new_liquid_level;
} }
}catch(InvalidPositionException &e)
{
}
} //for } //for
/* /*
@ -1645,20 +1699,13 @@ void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
}; };
for(u16 i=0; i<6; i++) for(u16 i=0; i<6; i++)
{ {
try
{
v3s16 p2 = p0 + dirs[i]; v3s16 p2 = p0 + dirs[i];
MapNode n2 = getNode(p2); MapNode n2 = getNodeNoEx(p2);
if(content_flowing_liquid(n2.d)) if(content_flowing_liquid(n2.d))
{ {
m_transforming_liquid.push_back(p2); m_transforming_liquid.push_back(p2);
} }
}catch(InvalidPositionException &e)
{
}
} }
} }
} }
@ -1679,9 +1726,6 @@ void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
}; };
for(u16 i=0; i<5; i++) for(u16 i=0; i<5; i++)
{ {
try
{
bool to_bottom = (i == 0); bool to_bottom = (i == 0);
// If liquid is at lowest possible height, it's not going // If liquid is at lowest possible height, it's not going
@ -1707,7 +1751,7 @@ void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
v3s16 p2 = p0 + dirs_to[i]; v3s16 p2 = p0 + dirs_to[i];
MapNode n2 = getNode(p2); MapNode n2 = getNodeNoEx(p2);
//dstream<<"[1] n2.param="<<(int)n2.param<<std::endl; //dstream<<"[1] n2.param="<<(int)n2.param<<std::endl;
if(content_liquid(n2.d)) if(content_liquid(n2.d))
@ -1773,10 +1817,6 @@ void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
// If n2_changed to bottom, don't flow anywhere else // If n2_changed to bottom, don't flow anywhere else
if(to_bottom && flowed && !is_source) if(to_bottom && flowed && !is_source)
break; break;
}catch(InvalidPositionException &e)
{
}
} }
loopcount++; loopcount++;
@ -1972,7 +2012,6 @@ ServerMap::~ServerMap()
{ {
if(m_map_saving_enabled) if(m_map_saving_enabled)
{ {
//save(false);
// Save only changed parts // Save only changed parts
save(true); save(true);
dstream<<DTIME<<"Server: saved map to "<<m_savedir<<std::endl; dstream<<DTIME<<"Server: saved map to "<<m_savedir<<std::endl;
@ -2079,6 +2118,8 @@ MapBlock* ServerMap::finishBlockMake(mapgen::BlockMakeData *data,
return NULL; return NULL;
} }
bool enable_mapgen_debug_info = g_settings.getBool("enable_mapgen_debug_info");
/*dstream<<"Resulting vmanip:"<<std::endl; /*dstream<<"Resulting vmanip:"<<std::endl;
data->vmanip.print(dstream);*/ data->vmanip.print(dstream);*/
@ -2091,10 +2132,11 @@ MapBlock* ServerMap::finishBlockMake(mapgen::BlockMakeData *data,
//TimeTaker timer("finishBlockMake() blitBackAll"); //TimeTaker timer("finishBlockMake() blitBackAll");
data->vmanip->blitBackAll(&changed_blocks); data->vmanip->blitBackAll(&changed_blocks);
} }
#if 1
dstream<<"finishBlockMake: changed_blocks.size()=" if(enable_mapgen_debug_info)
<<changed_blocks.size()<<std::endl; dstream<<"finishBlockMake: changed_blocks.size()="
#endif <<changed_blocks.size()<<std::endl;
/* /*
Copy transforming liquid information Copy transforming liquid information
*/ */
@ -2128,28 +2170,38 @@ MapBlock* ServerMap::finishBlockMake(mapgen::BlockMakeData *data,
/* /*
NOTE: Lighting and object adding shouldn't really be here, but NOTE: Lighting and object adding shouldn't really be here, but
lighting is a bit tricky to move properly to makeBlock. lighting is a bit tricky to move properly to makeBlock.
TODO: Do this the right way anyway. TODO: Do this the right way anyway, that is, move it to makeBlock.
- There needs to be some way for makeBlock to report back if
the lighting update is going further down because of the
new block blocking light
*/ */
/* /*
Update lighting Update lighting
NOTE: This takes ~60ms, TODO: Investigate why
*/ */
core::map<v3s16, MapBlock*> lighting_update_blocks;
// Center block
lighting_update_blocks.insert(block->getPos(), block);
#if 0
// All modified blocks
for(core::map<v3s16, MapBlock*>::Iterator
i = changed_blocks.getIterator();
i.atEnd() == false; i++)
{ {
lighting_update_blocks.insert(i.getNode()->getKey(), TimeTaker t("finishBlockMake lighting update");
i.getNode()->getValue());
core::map<v3s16, MapBlock*> lighting_update_blocks;
// Center block
lighting_update_blocks.insert(block->getPos(), block);
#if 0
// All modified blocks
for(core::map<v3s16, MapBlock*>::Iterator
i = changed_blocks.getIterator();
i.atEnd() == false; i++)
{
lighting_update_blocks.insert(i.getNode()->getKey(),
i.getNode()->getValue());
}
#endif
updateLighting(lighting_update_blocks, changed_blocks);
if(enable_mapgen_debug_info == false)
t.stop(true); // Hide output
} }
#endif
updateLighting(lighting_update_blocks, changed_blocks);
/* /*
Add random objects to block Add random objects to block
*/ */
@ -2262,6 +2314,8 @@ MapBlock * ServerMap::generateBlock(
<<"("<<p.X<<","<<p.Y<<","<<p.Z<<")" <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
<<std::endl;*/ <<std::endl;*/
bool enable_mapgen_debug_info = g_settings.getBool("enable_mapgen_debug_info");
TimeTaker timer("generateBlock"); TimeTaker timer("generateBlock");
//MapBlock *block = original_dummy; //MapBlock *block = original_dummy;
@ -2290,6 +2344,9 @@ MapBlock * ServerMap::generateBlock(
{ {
TimeTaker t("mapgen::make_block()"); TimeTaker t("mapgen::make_block()");
mapgen::make_block(&data); mapgen::make_block(&data);
if(enable_mapgen_debug_info == false)
t.stop(true); // Hide output
} }
/* /*
@ -2348,6 +2405,9 @@ MapBlock * ServerMap::generateBlock(
} }
#endif #endif
if(enable_mapgen_debug_info == false)
timer.stop(true); // Hide output
return block; return block;
} }
@ -2414,23 +2474,51 @@ MapBlock * ServerMap::createBlock(v3s16 p)
return block; return block;
} }
#if 0 MapBlock * ServerMap::emergeBlock(v3s16 p, bool allow_generate)
MapBlock * ServerMap::emergeBlock(
v3s16 p,
bool only_from_disk,
core::map<v3s16, MapBlock*> &changed_blocks,
core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
)
{ {
DSTACKF("%s: p=(%d,%d,%d), only_from_disk=%d", DSTACKF("%s: p=(%d,%d,%d), allow_generate=%d",
__FUNCTION_NAME, __FUNCTION_NAME,
p.X, p.Y, p.Z, only_from_disk); p.X, p.Y, p.Z, allow_generate);
// This has to be redone or removed {
assert(0); MapBlock *block = getBlockNoCreateNoEx(p);
if(block)
return block;
}
{
MapBlock *block = loadBlock(p);
if(block)
return block;
}
if(allow_generate)
{
core::map<v3s16, MapBlock*> modified_blocks;
MapBlock *block = generateBlock(p, modified_blocks);
if(block)
{
MapEditEvent event;
event.type = MEET_OTHER;
event.p = p;
// Copy modified_blocks to event
for(core::map<v3s16, MapBlock*>::Iterator
i = modified_blocks.getIterator();
i.atEnd()==false; i++)
{
event.modified_blocks.insert(i.getNode()->getKey(), false);
}
// Queue event
dispatchEvent(&event);
return block;
}
}
return NULL; return NULL;
} }
#endif
#if 0 #if 0
/* /*
@ -2877,10 +2965,10 @@ MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load
// format. Just go ahead and create the sector. // format. Just go ahead and create the sector.
if(fs::PathExists(sectordir)) if(fs::PathExists(sectordir))
{ {
dstream<<"ServerMap::loadSectorMeta(): Sector metafile " /*dstream<<"ServerMap::loadSectorMeta(): Sector metafile "
<<fullpath<<" doesn't exist but directory does." <<fullpath<<" doesn't exist but directory does."
<<" Continuing with a sector with no metadata." <<" Continuing with a sector with no metadata."
<<std::endl; <<std::endl;*/
sector = new ServerMapSector(this, p2d); sector = new ServerMapSector(this, p2d);
m_sectors.insert(p2d, sector); m_sectors.insert(p2d, sector);
} }
@ -3094,10 +3182,8 @@ void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSecto
MapBlock *block = NULL; MapBlock *block = NULL;
bool created_new = false; bool created_new = false;
try{ block = sector->getBlockNoCreateNoEx(p3d.Y);
block = sector->getBlockNoCreate(p3d.Y); if(block == NULL)
}
catch(InvalidPositionException &e)
{ {
block = sector->createBlankBlockNoInsert(p3d.Y); block = sector->createBlankBlockNoInsert(p3d.Y);
created_new = true; created_new = true;
@ -3267,6 +3353,7 @@ MapSector * ClientMap::emergeSector(v2s16 p2d)
return sector; return sector;
} }
#if 0
void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is) void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
{ {
DSTACK(__FUNCTION_NAME); DSTACK(__FUNCTION_NAME);
@ -3292,6 +3379,7 @@ void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
sector->deSerialize(is); sector->deSerialize(is);
} }
#endif
void ClientMap::OnRegisterSceneNode() void ClientMap::OnRegisterSceneNode()
{ {
@ -3350,13 +3438,13 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
// Take a fair amount as we will be dropping more out later // Take a fair amount as we will be dropping more out later
v3s16 p_blocks_min( v3s16 p_blocks_min(
p_nodes_min.X / MAP_BLOCKSIZE - 1, p_nodes_min.X / MAP_BLOCKSIZE - 2,
p_nodes_min.Y / MAP_BLOCKSIZE - 1, p_nodes_min.Y / MAP_BLOCKSIZE - 2,
p_nodes_min.Z / MAP_BLOCKSIZE - 1); p_nodes_min.Z / MAP_BLOCKSIZE - 2);
v3s16 p_blocks_max( v3s16 p_blocks_max(
p_nodes_max.X / MAP_BLOCKSIZE, p_nodes_max.X / MAP_BLOCKSIZE + 1,
p_nodes_max.Y / MAP_BLOCKSIZE, p_nodes_max.Y / MAP_BLOCKSIZE + 1,
p_nodes_max.Z / MAP_BLOCKSIZE); p_nodes_max.Z / MAP_BLOCKSIZE + 1);
u32 vertex_count = 0; u32 vertex_count = 0;
@ -3428,6 +3516,9 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
{ {
continue; continue;
} }
// Okay, this block will be drawn. Reset usage timer.
block->resetUsageTimer();
// This is ugly (spherical distance limit?) // This is ugly (spherical distance limit?)
/*if(m_control.range_all == false && /*if(m_control.range_all == false &&

136
src/map.h

@ -21,25 +21,21 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#define MAP_HEADER #define MAP_HEADER
#include <jmutex.h> #include <jmutex.h>
#include <jmutexautolock.h>
#include <jthread.h> #include <jthread.h>
#include <iostream> #include <iostream>
#ifdef _WIN32
#include <windows.h>
#define sleep_s(x) Sleep((x*1000))
#else
#include <unistd.h>
#define sleep_s(x) sleep(x)
#endif
#include "common_irrlicht.h" #include "common_irrlicht.h"
#include "mapnode.h" #include "mapnode.h"
#include "mapblock.h" #include "mapblock_nodemod.h"
#include "mapsector.h"
#include "constants.h" #include "constants.h"
#include "voxel.h" #include "voxel.h"
#include "mapchunk.h"
#include "nodemetadata.h" class MapSector;
class ServerMapSector;
class ClientMapSector;
class MapBlock;
class NodeMetadata;
namespace mapgen{ namespace mapgen{
struct BlockMakeData; struct BlockMakeData;
@ -61,7 +57,7 @@ enum MapEditEventType{
// Node metadata of block changed (not knowing which node exactly) // Node metadata of block changed (not knowing which node exactly)
// p stores block coordinate // p stores block coordinate
MEET_BLOCK_NODE_METADATA_CHANGED, MEET_BLOCK_NODE_METADATA_CHANGED,
// Anything else // Anything else (modified_blocks are set unsent)
MEET_OTHER MEET_OTHER
}; };
@ -104,17 +100,17 @@ public:
virtual void onMapEditEvent(MapEditEvent *event) = 0; virtual void onMapEditEvent(MapEditEvent *event) = 0;
}; };
class Map : public NodeContainer class Map /*: public NodeContainer*/
{ {
public: public:
Map(std::ostream &dout); Map(std::ostream &dout);
virtual ~Map(); virtual ~Map();
virtual u16 nodeContainerId() const /*virtual u16 nodeContainerId() const
{ {
return NODECONTAINER_ID_MAP; return NODECONTAINER_ID_MAP;
} }*/
virtual s32 mapType() const virtual s32 mapType() const
{ {
@ -155,66 +151,20 @@ public:
MapBlock * getBlockNoCreate(v3s16 p); MapBlock * getBlockNoCreate(v3s16 p);
// Returns NULL if not found // Returns NULL if not found
MapBlock * getBlockNoCreateNoEx(v3s16 p); MapBlock * getBlockNoCreateNoEx(v3s16 p);
// Gets an existing block or creates an empty one
//MapBlock * getBlockCreate(v3s16 p);
// Returns InvalidPositionException if not found // Returns InvalidPositionException if not found
bool isNodeUnderground(v3s16 p); bool isNodeUnderground(v3s16 p);
// virtual from NodeContainer bool isValidPosition(v3s16 p);
bool isValidPosition(v3s16 p)
{
v3s16 blockpos = getNodeBlockPos(p);
MapBlock *blockref;
try{
blockref = getBlockNoCreate(blockpos);
}
catch(InvalidPositionException &e)
{
return false;
}
return true;
/*v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
bool is_valid = blockref->isValidPosition(relpos);
return is_valid;*/
}
// virtual from NodeContainer
// throws InvalidPositionException if not found // throws InvalidPositionException if not found
MapNode getNode(v3s16 p) 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
// throws InvalidPositionException if not found // throws InvalidPositionException if not found
void setNode(v3s16 p, MapNode & n) void setNode(v3s16 p, MapNode & n);
{
v3s16 blockpos = getNodeBlockPos(p);
MapBlock * blockref = getBlockNoCreate(blockpos);
v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
blockref->setNodeNoCheck(relpos, n);
}
// Returns a CONTENT_IGNORE node if not found // Returns a CONTENT_IGNORE node if not found
MapNode getNodeNoEx(v3s16 p) MapNode getNodeNoEx(v3s16 p);
{
try{
v3s16 blockpos = getNodeBlockPos(p);
MapBlock * blockref = getBlockNoCreate(blockpos);
v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
return blockref->getNodeNoCheck(relpos);
}
catch(InvalidPositionException &e)
{
return MapNode(CONTENT_IGNORE);
}
}
void unspreadLight(enum LightBank bank, void unspreadLight(enum LightBank bank,
core::map<v3s16, u8> & from_nodes, core::map<v3s16, u8> & from_nodes,
@ -273,23 +223,33 @@ public:
virtual void save(bool only_changed){assert(0);}; virtual void save(bool only_changed){assert(0);};
// Server implements this // Server implements this.
// Client leaves it as no-op.
virtual void saveBlock(MapBlock *block){}; virtual void saveBlock(MapBlock *block){};
/* /*
Updates usage timers Updates usage timers and unloads unused blocks and sectors.
Saves modified blocks before unloading on MAPTYPE_SERVER.
*/ */
void timerUpdate(float dtime); void timerUpdate(float dtime, float unload_timeout,
core::list<v3s16> *unloaded_blocks=NULL);
// Deletes sectors and their blocks from memory
// Takes cache into account // Takes cache into account
// sector mutex should be locked when calling // If deleted sector is in sector cache, clears cache
void deleteSectors(core::list<v2s16> &list, bool only_blocks); void deleteSectors(core::list<v2s16> &list);
// Returns count of deleted sectors
u32 unloadUnusedData(float timeout, bool only_blocks=false,
core::list<v3s16> *deleted_blocks=NULL);
// For debug printing #if 0
/*
Unload unused data
= flush changed to disk and delete from memory, if usage timer of
block is more than timeout
*/
void unloadUnusedData(float timeout,
core::list<v3s16> *deleted_blocks=NULL);
#endif
// For debug printing. Prints "Map: ", "ServerMap: " or "ClientMap: "
virtual void PrintInfo(std::ostream &out); virtual void PrintInfo(std::ostream &out);
void transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks); void transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks);
@ -321,7 +281,6 @@ protected:
core::map<MapEventReceiver*, bool> m_event_receivers; core::map<MapEventReceiver*, bool> m_event_receivers;
core::map<v2s16, MapSector*> m_sectors; core::map<v2s16, MapSector*> m_sectors;
//JMutex m_sector_mutex;
// Be sure to set this to NULL when the cached sector is deleted // Be sure to set this to NULL when the cached sector is deleted
MapSector *m_sector_cache; MapSector *m_sector_cache;
@ -379,24 +338,13 @@ public:
*/ */
MapBlock * createBlock(v3s16 p); MapBlock * createBlock(v3s16 p);
#if 0
/* /*
NOTE: This comment might be outdated
Forcefully get a block from somewhere. Forcefully get a block from somewhere.
- Memory
InvalidPositionException possible if only_from_disk==true - Load from disk
- Generate
Parameters:
changed_blocks: Blocks that have been modified
*/ */
MapBlock * emergeBlock( MapBlock * emergeBlock(v3s16 p, bool allow_generate=true);
v3s16 p,
bool only_from_disk,
core::map<v3s16, MapBlock*> &changed_blocks,
core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
);
#endif
// Helper for placing objects on ground level // Helper for placing objects on ground level
s16 findGroundLevel(v2s16 p2d); s16 findGroundLevel(v2s16 p2d);
@ -547,7 +495,7 @@ public:
*/ */
MapSector * emergeSector(v2s16 p); MapSector * emergeSector(v2s16 p);
void deSerializeSector(v2s16 p2d, std::istream &is); //void deSerializeSector(v2s16 p2d, std::istream &is);
/* /*
ISceneNode methods ISceneNode methods

@ -28,7 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
MapBlock MapBlock
*/ */
MapBlock::MapBlock(NodeContainer *parent, v3s16 pos, bool dummy): MapBlock::MapBlock(Map *parent, v3s16 pos, bool dummy):
m_parent(parent), m_parent(parent),
m_pos(pos), m_pos(pos),
m_modified(MOD_STATE_WRITE_NEEDED), m_modified(MOD_STATE_WRITE_NEEDED),
@ -38,7 +38,7 @@ MapBlock::MapBlock(NodeContainer *parent, v3s16 pos, bool dummy):
m_generated(false), m_generated(false),
m_objects(this), m_objects(this),
m_timestamp(BLOCK_TIMESTAMP_UNDEFINED), m_timestamp(BLOCK_TIMESTAMP_UNDEFINED),
m_usage_timer(BLOCK_TIMESTAMP_UNDEFINED) m_usage_timer(0)
{ {
data = NULL; data = NULL;
if(dummy == false) if(dummy == false)
@ -607,24 +607,20 @@ void MapBlock::serialize(std::ostream &os, u8 version)
Get data Get data
*/ */
// Serialize nodes
SharedBuffer<u8> databuf_nodelist(nodecount*3);
for(u32 i=0; i<nodecount; i++)
{
data[i].serialize(&databuf_nodelist[i*3], version);
}
// Create buffer with different parameters sorted
SharedBuffer<u8> databuf(nodecount*3); SharedBuffer<u8> databuf(nodecount*3);
// Get contents
for(u32 i=0; i<nodecount; i++) for(u32 i=0; i<nodecount; i++)
{ {
databuf[i] = data[i].d; databuf[i] = databuf_nodelist[i*3];
} databuf[i+nodecount] = databuf_nodelist[i*3+1];
databuf[i+nodecount*2] = databuf_nodelist[i*3+2];
// Get params
for(u32 i=0; i<nodecount; i++)
{
databuf[i+nodecount] = data[i].param;
}
// Get param2
for(u32 i=0; i<nodecount; i++)
{
databuf[i+nodecount*2] = data[i].param2;
} }
/* /*
@ -773,20 +769,14 @@ void MapBlock::deSerialize(std::istream &is, u8 version)
("MapBlock::deSerialize: decompress resulted in size" ("MapBlock::deSerialize: decompress resulted in size"
" other than nodecount*3"); " other than nodecount*3");
// Set contents // deserialize nodes from buffer
for(u32 i=0; i<nodecount; i++) for(u32 i=0; i<nodecount; i++)
{ {
data[i].d = s[i]; u8 buf[3];
} buf[0] = s[i];
// Set params buf[1] = s[i+nodecount];
for(u32 i=0; i<nodecount; i++) buf[2] = s[i+nodecount*2];
{ data[i].deSerialize(buf, version);
data[i].param = s[i+nodecount];
}
// Set param2
for(u32 i=0; i<nodecount; i++)
{
data[i].param2 = s[i+nodecount*2];
} }
/* /*
@ -818,25 +808,6 @@ void MapBlock::deSerialize(std::istream &is, u8 version)
} }
} }
} }
/*
Translate nodes as specified in the translate_to fields of
node features
NOTE: This isn't really used. Should it be removed?
*/
for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
{
MapNode &n = data[i];
MapNode *translate_to = content_features(n.d).translate_to;
if(translate_to)
{
dstream<<"MapBlock: WARNING: Translating node "<<n.d<<" to "
<<translate_to->d<<std::endl;
n = *translate_to;
}
}
} }
void MapBlock::serializeDiskExtra(std::ostream &os, u8 version) void MapBlock::serializeDiskExtra(std::ostream &os, u8 version)

@ -38,6 +38,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "mapblock_mesh.h" #include "mapblock_mesh.h"
#endif #endif
class Map;
#define BLOCK_TIMESTAMP_UNDEFINED 0xffffffff #define BLOCK_TIMESTAMP_UNDEFINED 0xffffffff
@ -81,6 +82,7 @@ enum ModifiedState
BLOCKGEN_FULLY_GENERATED=6 BLOCKGEN_FULLY_GENERATED=6
};*/ };*/
#if 0
enum enum
{ {
NODECONTAINER_ID_MAPBLOCK, NODECONTAINER_ID_MAPBLOCK,
@ -108,23 +110,24 @@ public:
} }
} }
}; };
#endif
/* /*
MapBlock itself MapBlock itself
*/ */
class MapBlock : public NodeContainer class MapBlock /*: public NodeContainer*/
{ {
public: public:
MapBlock(NodeContainer *parent, v3s16 pos, bool dummy=false); MapBlock(Map *parent, v3s16 pos, bool dummy=false);
~MapBlock(); ~MapBlock();
virtual u16 nodeContainerId() const /*virtual u16 nodeContainerId() const
{ {
return NODECONTAINER_ID_MAPBLOCK; return NODECONTAINER_ID_MAPBLOCK;
} }*/
NodeContainer * getParent() Map * getParent()
{ {
return m_parent; return m_parent;
} }
@ -640,7 +643,7 @@ private:
*/ */
// NOTE: Lots of things rely on this being the Map // NOTE: Lots of things rely on this being the Map
NodeContainer *m_parent; Map *m_parent;
// Position in blocks on parent // Position in blocks on parent
v3s16 m_pos; v3s16 m_pos;

@ -64,11 +64,7 @@ void MeshMakeData::fill(u32 daynight_ratio, MapBlock *block)
*/ */
// Get map // Get map
NodeContainer *parentcontainer = block->getParent(); Map *map = block->getParent();
// This will only work if the parent is the map
assert(parentcontainer->nodeContainerId() == NODECONTAINER_ID_MAP);
// OK, we have the map!
Map *map = (Map*)parentcontainer;
for(u16 i=0; i<6; i++) for(u16 i=0; i<6; i++)
{ {

@ -25,6 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "map.h" #include "map.h"
#include "inventory.h" #include "inventory.h"
#include "utility.h" #include "utility.h"
#include "mapblock.h"
/* /*
MapBlockObject MapBlockObject
@ -856,16 +857,7 @@ bool MapBlockObjectList::wrapObject(MapBlockObject *object)
assert(m_objects.find(object->m_id) != NULL); assert(m_objects.find(object->m_id) != NULL);
assert(m_objects[object->m_id] == object); assert(m_objects[object->m_id] == object);
NodeContainer *parentcontainer = m_block->getParent(); Map *map = m_block->getParent();
// This will only work if the parent is the map
if(parentcontainer->nodeContainerId() != NODECONTAINER_ID_MAP)
{
dstream<<"WARNING: Wrapping object not possible: "
"MapBlock's parent is not map"<<std::endl;
return true;
}
// OK, we have the map!
Map *map = (Map*)parentcontainer;
// Calculate blockpos on map // Calculate blockpos on map
v3s16 oldblock_pos_i_on_map = m_block->getPosRelative(); v3s16 oldblock_pos_i_on_map = m_block->getPosRelative();

@ -20,6 +20,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#ifndef MAPCHUNK_HEADER #ifndef MAPCHUNK_HEADER
#define MAPCHUNK_HEADER #define MAPCHUNK_HEADER
/*
TODO: Remove
*/
#if 0
/* /*
MapChunk contains map-generation-time metadata for an area of MapChunk contains map-generation-time metadata for an area of
some MapSectors. (something like 16x16) some MapSectors. (something like 16x16)
@ -66,6 +71,7 @@ private:
u8 m_generation_level; u8 m_generation_level;
bool m_modified; bool m_modified;
}; };
#endif
#endif #endif

@ -23,8 +23,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "noise.h" #include "noise.h"
#include "mapblock.h" #include "mapblock.h"
#include "map.h" #include "map.h"
#include "serverobject.h"
#include "mineral.h" #include "mineral.h"
//#include "serverobject.h"
#include "content_sao.h"
namespace mapgen namespace mapgen
{ {
@ -531,7 +532,7 @@ static void make_corridor(VoxelManipulator &vmanip, v3s16 doorplace,
else else
length = random.range(1,6); length = random.range(1,6);
length = random.range(1,13); length = random.range(1,13);
u32 partlength = random.range(1,length); u32 partlength = random.range(1,13);
u32 partcount = 0; u32 partcount = 0;
s16 make_stairs = 0; s16 make_stairs = 0;
if(random.next()%2 == 0 && partlength >= 3) if(random.next()%2 == 0 && partlength >= 3)
@ -700,14 +701,30 @@ public:
continue; continue;
v3s16 roomplace; v3s16 roomplace;
// X east, Z north, Y up // X east, Z north, Y up
#if 1
if(doordir == v3s16(1,0,0)) // X+ if(doordir == v3s16(1,0,0)) // X+
roomplace = doorplace + v3s16(0,-1,-roomsize.Z/2+m_random.range(-roomsize.Z/2+1,roomsize.Z/2-1)); roomplace = doorplace +
v3s16(0,-1,m_random.range(-roomsize.Z+2,-2));
if(doordir == v3s16(-1,0,0)) // X- if(doordir == v3s16(-1,0,0)) // X-
roomplace = doorplace + v3s16(-roomsize.X+1,-1,-roomsize.Z/2+m_random.range(-roomsize.Z/2+1,roomsize.Z/2-1)); roomplace = doorplace +
v3s16(-roomsize.X+1,-1,m_random.range(-roomsize.Z+2,-2));
if(doordir == v3s16(0,0,1)) // Z+ if(doordir == v3s16(0,0,1)) // Z+
roomplace = doorplace + v3s16(-roomsize.X/2+m_random.range(-roomsize.X/2+1,roomsize.X/2-1),-1,0); roomplace = doorplace +
v3s16(m_random.range(-roomsize.X+2,-2),-1,0);
if(doordir == v3s16(0,0,-1)) // Z- if(doordir == v3s16(0,0,-1)) // Z-
roomplace = doorplace + v3s16(-roomsize.X/2+m_random.range(-roomsize.X/2+1,roomsize.X/2-1),-1,-roomsize.Z+1); roomplace = doorplace +
v3s16(m_random.range(-roomsize.X+2,-2),-1,-roomsize.Z+1);
#endif
#if 0
if(doordir == v3s16(1,0,0)) // X+
roomplace = doorplace + v3s16(0,-1,-roomsize.Z/2);
if(doordir == v3s16(-1,0,0)) // X-
roomplace = doorplace + v3s16(-roomsize.X+1,-1,-roomsize.Z/2);
if(doordir == v3s16(0,0,1)) // Z+
roomplace = doorplace + v3s16(-roomsize.X/2,-1,0);
if(doordir == v3s16(0,0,-1)) // Z-
roomplace = doorplace + v3s16(-roomsize.X/2,-1,-roomsize.Z+1);
#endif
// Check fit // Check fit
bool fits = true; bool fits = true;
@ -818,7 +835,7 @@ static void make_dungeon1(VoxelManipulator &vmanip, PseudoRandom &random)
// Determine walker start position // Determine walker start position
bool start_in_last_room = (random.range(0,1)==0); bool start_in_last_room = (random.range(0,2)!=0);
//bool start_in_last_room = true; //bool start_in_last_room = true;
v3s16 walker_start_place; v3s16 walker_start_place;
@ -877,30 +894,47 @@ static void make_dungeon1(VoxelManipulator &vmanip, PseudoRandom &random)
Noise functions. Make sure seed is mangled differently in each one. Noise functions. Make sure seed is mangled differently in each one.
*/ */
// This affects the shape of the contour /*
Scaling the output of the noise function affects the overdrive of the
contour function, which affects the shape of the output considerably.
*/
#define CAVE_NOISE_SCALE 12.0
//#define CAVE_NOISE_SCALE 10.0 //#define CAVE_NOISE_SCALE 10.0
//#define CAVE_NOISE_SCALE 7.5 //#define CAVE_NOISE_SCALE 7.5
#define CAVE_NOISE_SCALE 5.0 //#define CAVE_NOISE_SCALE 5.0
//#define CAVE_NOISE_SCALE 1.0
//#define CAVE_NOISE_THRESHOLD (2.5/CAVE_NOISE_SCALE)
#define CAVE_NOISE_THRESHOLD (1.5/CAVE_NOISE_SCALE)
NoiseParams get_cave_noise1_params(u64 seed) NoiseParams get_cave_noise1_params(u64 seed)
{ {
/*return NoiseParams(NOISE_PERLIN_CONTOUR, seed+52534, 5, 0.7, /*return NoiseParams(NOISE_PERLIN_CONTOUR, seed+52534, 5, 0.7,
200, CAVE_NOISE_SCALE);*/ 200, CAVE_NOISE_SCALE);*/
return NoiseParams(NOISE_PERLIN_CONTOUR, seed+52534, 4, 0.7, /*return NoiseParams(NOISE_PERLIN_CONTOUR, seed+52534, 4, 0.7,
100, CAVE_NOISE_SCALE); 100, CAVE_NOISE_SCALE);*/
/*return NoiseParams(NOISE_PERLIN_CONTOUR, seed+52534, 5, 0.6,
100, CAVE_NOISE_SCALE);*/
/*return NoiseParams(NOISE_PERLIN_CONTOUR, seed+52534, 5, 0.3,
100, CAVE_NOISE_SCALE);*/
return NoiseParams(NOISE_PERLIN_CONTOUR, seed+52534, 4, 0.5,
50, CAVE_NOISE_SCALE);
//return NoiseParams(NOISE_CONSTANT_ONE);
} }
NoiseParams get_cave_noise2_params(u64 seed) NoiseParams get_cave_noise2_params(u64 seed)
{ {
/*return NoiseParams(NOISE_PERLIN_CONTOUR_FLIP_YZ, seed+10325, 5, 0.7, /*return NoiseParams(NOISE_PERLIN_CONTOUR_FLIP_YZ, seed+10325, 5, 0.7,
200, CAVE_NOISE_SCALE);*/ 200, CAVE_NOISE_SCALE);*/
return NoiseParams(NOISE_PERLIN_CONTOUR_FLIP_YZ, seed+10325, 4, 0.7, /*return NoiseParams(NOISE_PERLIN_CONTOUR_FLIP_YZ, seed+10325, 4, 0.7,
100, CAVE_NOISE_SCALE); 100, CAVE_NOISE_SCALE);*/
/*return NoiseParams(NOISE_PERLIN_CONTOUR_FLIP_YZ, seed+10325, 5, 0.3,
100, CAVE_NOISE_SCALE);*/
return NoiseParams(NOISE_PERLIN_CONTOUR_FLIP_YZ, seed+10325, 4, 0.5,
50, CAVE_NOISE_SCALE);
//return NoiseParams(NOISE_CONSTANT_ONE);
} }
//#define CAVE_NOISE_THRESHOLD (2.5/CAVE_NOISE_SCALE)
#define CAVE_NOISE_THRESHOLD (2.0/CAVE_NOISE_SCALE)
NoiseParams get_ground_noise1_params(u64 seed) NoiseParams get_ground_noise1_params(u64 seed)
{ {
return NoiseParams(NOISE_PERLIN, seed+983240, 4, return NoiseParams(NOISE_PERLIN, seed+983240, 4,
@ -937,13 +971,13 @@ bool val_is_ground(double ground_noise1_val, v3s16 p, u64 seed)
{ {
//return ((double)p.Y < ground_noise1_val); //return ((double)p.Y < ground_noise1_val);
double f = 0.8 + noise2d_perlin( double f = 0.55 + noise2d_perlin(
0.5+(float)p.X/250, 0.5+(float)p.Z/250, 0.5+(float)p.X/250, 0.5+(float)p.Z/250,
seed+920381, 3, 0.45); seed+920381, 3, 0.45);
if(f < 0.01) if(f < 0.01)
f = 0.01; f = 0.01;
else if(f >= 1.0) else if(f >= 1.0)
f *= 2.0; f *= 1.6;
double h = WATER_LEVEL + 10 * noise2d_perlin( double h = WATER_LEVEL + 10 * noise2d_perlin(
0.5+(float)p.X/250, 0.5+(float)p.Z/250, 0.5+(float)p.X/250, 0.5+(float)p.Z/250,
seed+84174, 4, 0.5); seed+84174, 4, 0.5);
@ -1082,6 +1116,7 @@ double get_sector_maximum_ground_level(u64 seed, v2s16 sectorpos, double p)
v2s16 node_min = sectorpos*MAP_BLOCKSIZE; v2s16 node_min = sectorpos*MAP_BLOCKSIZE;
v2s16 node_max = (sectorpos+v2s16(1,1))*MAP_BLOCKSIZE-v2s16(1,1); v2s16 node_max = (sectorpos+v2s16(1,1))*MAP_BLOCKSIZE-v2s16(1,1);
double a = -31000; double a = -31000;
// Corners
a = MYMAX(a, find_ground_level_from_noise(seed, a = MYMAX(a, find_ground_level_from_noise(seed,
v2s16(node_min.X, node_min.Y), p)); v2s16(node_min.X, node_min.Y), p));
a = MYMAX(a, find_ground_level_from_noise(seed, a = MYMAX(a, find_ground_level_from_noise(seed,
@ -1090,8 +1125,18 @@ double get_sector_maximum_ground_level(u64 seed, v2s16 sectorpos, double p)
v2s16(node_max.X, node_max.Y), p)); v2s16(node_max.X, node_max.Y), p));
a = MYMAX(a, find_ground_level_from_noise(seed, a = MYMAX(a, find_ground_level_from_noise(seed,
v2s16(node_min.X, node_min.Y), p)); v2s16(node_min.X, node_min.Y), p));
// Center
a = MYMAX(a, find_ground_level_from_noise(seed, a = MYMAX(a, find_ground_level_from_noise(seed,
v2s16(node_min.X+MAP_BLOCKSIZE/2, node_min.Y+MAP_BLOCKSIZE/2), p)); v2s16(node_min.X+MAP_BLOCKSIZE/2, node_min.Y+MAP_BLOCKSIZE/2), p));
// Side middle points
a = MYMAX(a, find_ground_level_from_noise(seed,
v2s16(node_min.X+MAP_BLOCKSIZE/2, node_min.Y), p));
a = MYMAX(a, find_ground_level_from_noise(seed,
v2s16(node_min.X+MAP_BLOCKSIZE/2, node_max.Y), p));
a = MYMAX(a, find_ground_level_from_noise(seed,
v2s16(node_min.X, node_min.Y+MAP_BLOCKSIZE/2), p));
a = MYMAX(a, find_ground_level_from_noise(seed,
v2s16(node_max.X, node_min.Y+MAP_BLOCKSIZE/2), p));
return a; return a;
} }
@ -1102,6 +1147,7 @@ double get_sector_minimum_ground_level(u64 seed, v2s16 sectorpos, double p)
v2s16 node_min = sectorpos*MAP_BLOCKSIZE; v2s16 node_min = sectorpos*MAP_BLOCKSIZE;
v2s16 node_max = (sectorpos+v2s16(1,1))*MAP_BLOCKSIZE-v2s16(1,1); v2s16 node_max = (sectorpos+v2s16(1,1))*MAP_BLOCKSIZE-v2s16(1,1);
double a = 31000; double a = 31000;
// Corners
a = MYMIN(a, find_ground_level_from_noise(seed, a = MYMIN(a, find_ground_level_from_noise(seed,
v2s16(node_min.X, node_min.Y), p)); v2s16(node_min.X, node_min.Y), p));
a = MYMIN(a, find_ground_level_from_noise(seed, a = MYMIN(a, find_ground_level_from_noise(seed,
@ -1110,8 +1156,18 @@ double get_sector_minimum_ground_level(u64 seed, v2s16 sectorpos, double p)
v2s16(node_max.X, node_max.Y), p)); v2s16(node_max.X, node_max.Y), p));
a = MYMIN(a, find_ground_level_from_noise(seed, a = MYMIN(a, find_ground_level_from_noise(seed,
v2s16(node_min.X, node_min.Y), p)); v2s16(node_min.X, node_min.Y), p));
// Center
a = MYMIN(a, find_ground_level_from_noise(seed, a = MYMIN(a, find_ground_level_from_noise(seed,
v2s16(node_min.X+MAP_BLOCKSIZE/2, node_min.Y+MAP_BLOCKSIZE/2), p)); v2s16(node_min.X+MAP_BLOCKSIZE/2, node_min.Y+MAP_BLOCKSIZE/2), p));
// Side middle points
a = MYMIN(a, find_ground_level_from_noise(seed,
v2s16(node_min.X+MAP_BLOCKSIZE/2, node_min.Y), p));
a = MYMIN(a, find_ground_level_from_noise(seed,
v2s16(node_min.X+MAP_BLOCKSIZE/2, node_max.Y), p));
a = MYMIN(a, find_ground_level_from_noise(seed,
v2s16(node_min.X, node_min.Y+MAP_BLOCKSIZE/2), p));
a = MYMIN(a, find_ground_level_from_noise(seed,
v2s16(node_max.X, node_min.Y+MAP_BLOCKSIZE/2), p));
return a; return a;
} }
@ -1358,12 +1414,12 @@ void make_block(BlockMakeData *data)
If block is deep underground, this is set to true and ground If block is deep underground, this is set to true and ground
density noise is not generated, for speed optimization. density noise is not generated, for speed optimization.
*/ */
bool all_is_ground_except_caves = (minimum_ground_depth > 16); bool all_is_ground_except_caves = (minimum_ground_depth > 40);
/* /*
Create a block-specific seed Create a block-specific seed
*/ */
u32 blockseed = (data->seed%0x100000000) + full_node_min.Z*38134234 u32 blockseed = (u32)(data->seed%0x100000000) + full_node_min.Z*38134234
+ full_node_min.Y*42123 + full_node_min.X*23; + full_node_min.Y*42123 + full_node_min.X*23;
/* /*
@ -1385,13 +1441,13 @@ void make_block(BlockMakeData *data)
/* /*
Cave noise Cave noise
*/ */
#if 1
noisebuf_cave.create(get_cave_noise1_params(data->seed), noisebuf_cave.create(get_cave_noise1_params(data->seed),
minpos_f.X, minpos_f.Y, minpos_f.Z, minpos_f.X, minpos_f.Y, minpos_f.Z,
maxpos_f.X, maxpos_f.Y, maxpos_f.Z, maxpos_f.X, maxpos_f.Y, maxpos_f.Z,
4, 3, 4); 2, 2, 2);
noisebuf_cave.multiply(get_cave_noise2_params(data->seed)); noisebuf_cave.multiply(get_cave_noise2_params(data->seed));
#endif
/* /*
Ground noise Ground noise

@ -30,8 +30,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
ContentFeatures::~ContentFeatures() ContentFeatures::~ContentFeatures()
{ {
if(translate_to) /*if(translate_to)
delete translate_to; delete translate_to;*/
if(initial_metadata) if(initial_metadata)
delete initial_metadata; delete initial_metadata;
} }
@ -138,6 +138,17 @@ void init_mapnode()
f->tiles[j].material_type = initial_material_type; f->tiles[j].material_type = initial_material_type;
} }
/*
Initially set every block to be shown as an unknown block.
Don't touch CONTENT_IGNORE or CONTENT_AIR.
*/
for(u16 i=0; i<=253; i++)
{
ContentFeatures *f = &g_content_features[i];
f->setAllTextures("unknown_block.png");
f->dug_item = std::string("MaterialItem ")+itos(i)+" 1";
}
/* /*
Initialize mapnode content Initialize mapnode content
*/ */
@ -230,6 +241,94 @@ u8 MapNode::getMineral()
return MINERAL_NONE; return MINERAL_NONE;
} }
u32 MapNode::serializedLength(u8 version)
{
if(!ser_ver_supported(version))
throw VersionMismatchException("ERROR: MapNode format not supported");
if(version == 0)
return 1;
else if(version <= 9)
return 2;
else
return 3;
}
void MapNode::serialize(u8 *dest, u8 version)
{
if(!ser_ver_supported(version))
throw VersionMismatchException("ERROR: MapNode format not supported");
u8 actual_d = d;
// Convert from new version to old
if(version <= 18)
{
// In these versions, CONTENT_IGNORE and CONTENT_AIR
// are 255 and 254
if(d == CONTENT_IGNORE)
d = 255;
else if(d == CONTENT_AIR)
d = 254;
}
if(version == 0)
{
dest[0] = actual_d;
}
else if(version <= 9)
{
dest[0] = actual_d;
dest[1] = param;
}
else
{
dest[0] = actual_d;
dest[1] = param;
dest[2] = param2;
}
}
void MapNode::deSerialize(u8 *source, u8 version)
{
if(!ser_ver_supported(version))
throw VersionMismatchException("ERROR: MapNode format not supported");
if(version == 0)
{
d = source[0];
}
else if(version == 1)
{
d = source[0];
// This version doesn't support saved lighting
if(light_propagates() || light_source() > 0)
param = 0;
else
param = source[1];
}
else if(version <= 9)
{
d = source[0];
param = source[1];
}
else
{
d = source[0];
param = source[1];
param2 = source[2];
// Convert from old version to new
if(version <= 18)
{
// In these versions, CONTENT_IGNORE and CONTENT_AIR
// are 255 and 254
if(d == 255)
d = CONTENT_IGNORE;
else if(d == 254)
d = CONTENT_AIR;
}
}
}
/* /*
Gets lighting value at face of node Gets lighting value at face of node

@ -36,6 +36,13 @@ with this program; if not, write to the Free Software Foundation, Inc.,
- Tile = TileSpec at some side of a node of some content type - Tile = TileSpec at some side of a node of some content type
*/ */
/*
Ranges:
0x000...0x07f: param2 is fully usable
0x800...0xfff: param2 lower 4 bytes are free
*/
typedef u16 content_t;
/* /*
Initializes all kind of stuff in here. Initializes all kind of stuff in here.
Many things depend on this. Many things depend on this.
@ -59,14 +66,16 @@ void init_mapnode();
Doesn't create faces with anything and is considered being Doesn't create faces with anything and is considered being
out-of-map in the game map. out-of-map in the game map.
*/ */
#define CONTENT_IGNORE 255 //#define CONTENT_IGNORE 255
#define CONTENT_IGNORE 127
#define CONTENT_IGNORE_DEFAULT_PARAM 0 #define CONTENT_IGNORE_DEFAULT_PARAM 0
/* /*
The common material through which the player can walk and which The common material through which the player can walk and which
is transparent to light is transparent to light
*/ */
#define CONTENT_AIR 254 //#define CONTENT_AIR 254
#define CONTENT_AIR 126
/* /*
Content feature list Content feature list
@ -94,7 +103,7 @@ class NodeMetadata;
struct ContentFeatures struct ContentFeatures
{ {
// If non-NULL, content is translated to this when deserialized // If non-NULL, content is translated to this when deserialized
MapNode *translate_to; //MapNode *translate_to;
// Type of MapNode::param // Type of MapNode::param
ContentParamType param_type; ContentParamType param_type;
@ -156,7 +165,7 @@ struct ContentFeatures
void reset() void reset()
{ {
translate_to = NULL; //translate_to = NULL;
param_type = CPT_NONE; param_type = CPT_NONE;
inventory_texture = NULL; inventory_texture = NULL;
is_ground_content = false; is_ground_content = false;
@ -196,6 +205,8 @@ struct ContentFeatures
{ {
setTexture(i, name, alpha); setTexture(i, name, alpha);
} }
// Force inventory texture too
setInventoryTexture(name);
} }
void setTile(u16 i, const TileSpec &tile) void setTile(u16 i, const TileSpec &tile)
@ -399,20 +410,30 @@ enum LightBank
struct MapNode struct MapNode
{ {
// Content /*
u8 d; Main content
0x00-0x7f: Short content type
0x80-0xff: Long content type (param2>>4 makes up low bytes)
*/
union
{
u8 param0;
u8 d;
};
/* /*
Misc parameter. Initialized to 0. Misc parameter. Initialized to 0.
- For light_propagates() blocks, this is light intensity, - For light_propagates() blocks, this is light intensity,
stored logarithmically from 0 to LIGHT_MAX. stored logarithmically from 0 to LIGHT_MAX.
Sunlight is LIGHT_SUN, which is LIGHT_MAX+1. Sunlight is LIGHT_SUN, which is LIGHT_MAX+1.
- Contains 2 values, day- and night lighting. Each takes 4 bits. - Contains 2 values, day- and night lighting. Each takes 4 bits.
- Mineral content (should be removed from here)
- Uhh... well, most blocks have light or nothing in here.
*/ */
union union
{ {
s8 param;
u8 param1; u8 param1;
s8 param;
}; };
/* /*
@ -437,14 +458,6 @@ struct MapNode
param2 = a_param2; param2 = a_param2;
} }
/*MapNode & operator=(const MapNode &other)
{
d = other.d;
param = other.param;
param2 = other.param2;
return *this;
}*/
bool operator==(const MapNode &other) bool operator==(const MapNode &other)
{ {
return (d == other.d return (d == other.d
@ -452,6 +465,16 @@ struct MapNode
&& param2 == other.param2); && param2 == other.param2);
} }
// To be used everywhere
content_t getContent()
{
return d;
}
void setContent(content_t c)
{
d = c;
}
/* /*
These four are DEPRECATED I guess. -c55 These four are DEPRECATED I guess. -c55
*/ */
@ -566,88 +589,15 @@ struct MapNode
MINERAL_NONE if doesn't contain or isn't able to contain mineral. MINERAL_NONE if doesn't contain or isn't able to contain mineral.
*/ */
u8 getMineral(); u8 getMineral();
/* /*
These serialization functions are used when informing client Serialization functions
of a single node add.
NOTE: When loading a MapBlock, these are not used. Should they?
*/ */
static u32 serializedLength(u8 version) static u32 serializedLength(u8 version);
{ void serialize(u8 *dest, u8 version);
if(!ser_ver_supported(version)) void deSerialize(u8 *source, u8 version);
throw VersionMismatchException("ERROR: MapNode format not supported");
if(version == 0)
return 1;
else if(version <= 9)
return 2;
else
return 3;
}
void serialize(u8 *dest, u8 version)
{
if(!ser_ver_supported(version))
throw VersionMismatchException("ERROR: MapNode format not supported");
if(version == 0)
{
dest[0] = d;
}
else if(version <= 9)
{
dest[0] = d;
dest[1] = param;
}
else
{
dest[0] = d;
dest[1] = param;
dest[2] = param2;
}
}
void deSerialize(u8 *source, u8 version)
{
if(!ser_ver_supported(version))
throw VersionMismatchException("ERROR: MapNode format not supported");
if(version == 0)
{
d = source[0];
}
else if(version == 1)
{
d = source[0];
// This version doesn't support saved lighting
if(light_propagates() || light_source() > 0)
param = 0;
else
param = source[1];
}
else if(version <= 9)
{
d = source[0];
param = source[1];
}
else
{
d = source[0];
param = source[1];
param2 = source[2];
}
// Translate deprecated stuff
// NOTE: This doesn't get used because MapBlock handles node
// parameters directly
MapNode *translate_to = content_features(d).translate_to;
if(translate_to)
{
dstream<<"MapNode: WARNING: Translating "<<d<<" to "
<<translate_to->d<<std::endl;
*this = *translate_to;
}
}
}; };
/* /*

@ -21,15 +21,14 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "jmutexautolock.h" #include "jmutexautolock.h"
#include "client.h" #include "client.h"
#include "exceptions.h" #include "exceptions.h"
#include "mapblock.h"
MapSector::MapSector(NodeContainer *parent, v2s16 pos): MapSector::MapSector(Map *parent, v2s16 pos):
differs_from_disk(true), differs_from_disk(false),
m_parent(parent), m_parent(parent),
m_pos(pos), m_pos(pos),
m_block_cache(NULL) m_block_cache(NULL)
{ {
m_mutex.Init();
assert(m_mutex.IsInitialized());
} }
MapSector::~MapSector() MapSector::~MapSector()
@ -39,8 +38,6 @@ MapSector::~MapSector()
void MapSector::deleteBlocks() void MapSector::deleteBlocks()
{ {
JMutexAutoLock lock(m_mutex);
// Clear cache // Clear cache
m_block_cache = NULL; m_block_cache = NULL;
@ -83,26 +80,12 @@ MapBlock * MapSector::getBlockBuffered(s16 y)
MapBlock * MapSector::getBlockNoCreateNoEx(s16 y) MapBlock * MapSector::getBlockNoCreateNoEx(s16 y)
{ {
JMutexAutoLock lock(m_mutex);
return getBlockBuffered(y); return getBlockBuffered(y);
} }
MapBlock * MapSector::getBlockNoCreate(s16 y)
{
MapBlock *block = getBlockNoCreateNoEx(y);
if(block == NULL)
throw InvalidPositionException();
return block;
}
MapBlock * MapSector::createBlankBlockNoInsert(s16 y) MapBlock * MapSector::createBlankBlockNoInsert(s16 y)
{ {
// There should not be a block at this position assert(getBlockBuffered(y) == NULL);
if(getBlockBuffered(y) != NULL)
throw AlreadyExistsException("Block already exists");
v3s16 blockpos_map(m_pos.X, y, m_pos.Y); v3s16 blockpos_map(m_pos.X, y, m_pos.Y);
@ -113,8 +96,6 @@ MapBlock * MapSector::createBlankBlockNoInsert(s16 y)
MapBlock * MapSector::createBlankBlock(s16 y) MapBlock * MapSector::createBlankBlock(s16 y)
{ {
JMutexAutoLock lock(m_mutex);
MapBlock *block = createBlankBlockNoInsert(y); MapBlock *block = createBlankBlockNoInsert(y);
m_blocks.insert(y, block); m_blocks.insert(y, block);
@ -126,39 +107,34 @@ void MapSector::insertBlock(MapBlock *block)
{ {
s16 block_y = block->getPos().Y; s16 block_y = block->getPos().Y;
{ MapBlock *block2 = getBlockBuffered(block_y);
JMutexAutoLock lock(m_mutex); if(block2 != NULL){
throw AlreadyExistsException("Block already exists");
MapBlock *block2 = getBlockBuffered(block_y);
if(block2 != NULL){
throw AlreadyExistsException("Block already exists");
}
v2s16 p2d(block->getPos().X, block->getPos().Z);
assert(p2d == m_pos);
// Insert into container
m_blocks.insert(block_y, block);
} }
v2s16 p2d(block->getPos().X, block->getPos().Z);
assert(p2d == m_pos);
// Insert into container
m_blocks.insert(block_y, block);
} }
void MapSector::removeBlock(MapBlock *block) void MapSector::deleteBlock(MapBlock *block)
{ {
s16 block_y = block->getPos().Y; s16 block_y = block->getPos().Y;
JMutexAutoLock lock(m_mutex);
// Clear from cache // Clear from cache
m_block_cache = NULL; m_block_cache = NULL;
// Remove from container // Remove from container
m_blocks.remove(block_y); m_blocks.remove(block_y);
// Delete
delete block;
} }
void MapSector::getBlocks(core::list<MapBlock*> &dest) void MapSector::getBlocks(core::list<MapBlock*> &dest)
{ {
JMutexAutoLock lock(m_mutex);
core::list<MapBlock*> ref_list; core::list<MapBlock*> ref_list;
core::map<s16, MapBlock*>::Iterator bi; core::map<s16, MapBlock*>::Iterator bi;
@ -175,7 +151,7 @@ void MapSector::getBlocks(core::list<MapBlock*> &dest)
ServerMapSector ServerMapSector
*/ */
ServerMapSector::ServerMapSector(NodeContainer *parent, v2s16 pos): ServerMapSector::ServerMapSector(Map *parent, v2s16 pos):
MapSector(parent, pos) MapSector(parent, pos)
{ {
} }
@ -184,15 +160,6 @@ ServerMapSector::~ServerMapSector()
{ {
} }
f32 ServerMapSector::getGroundHeight(v2s16 p, bool generate)
{
return GROUNDHEIGHT_NOTFOUND_SETVALUE;
}
void ServerMapSector::setGroundHeight(v2s16 p, f32 y, bool generate)
{
}
void ServerMapSector::serialize(std::ostream &os, u8 version) void ServerMapSector::serialize(std::ostream &os, u8 version)
{ {
if(!ser_ver_supported(version)) if(!ser_ver_supported(version))
@ -217,7 +184,7 @@ void ServerMapSector::serialize(std::ostream &os, u8 version)
ServerMapSector* ServerMapSector::deSerialize( ServerMapSector* ServerMapSector::deSerialize(
std::istream &is, std::istream &is,
NodeContainer *parent, Map *parent,
v2s16 p2d, v2s16 p2d,
core::map<v2s16, MapSector*> & sectors core::map<v2s16, MapSector*> & sectors
) )
@ -280,7 +247,7 @@ ServerMapSector* ServerMapSector::deSerialize(
ClientMapSector ClientMapSector
*/ */
ClientMapSector::ClientMapSector(NodeContainer *parent, v2s16 pos): ClientMapSector::ClientMapSector(Map *parent, v2s16 pos):
MapSector(parent, pos) MapSector(parent, pos)
{ {
} }
@ -289,45 +256,6 @@ ClientMapSector::~ClientMapSector()
{ {
} }
void ClientMapSector::deSerialize(std::istream &is)
{
/*
[0] u8 serialization version
[1] s16 corners[0]
[3] s16 corners[1]
[5] s16 corners[2]
[7] s16 corners[3]
size = 9
In which corners are in these positions
v2s16(0,0),
v2s16(1,0),
v2s16(1,1),
v2s16(0,1),
*/
// Read version
u8 version = SER_FMT_VER_INVALID;
is.read((char*)&version, 1);
if(!ser_ver_supported(version))
throw VersionMismatchException("ERROR: MapSector format not supported");
u8 buf[2];
// Dummy read corners
is.read((char*)buf, 2);
is.read((char*)buf, 2);
is.read((char*)buf, 2);
is.read((char*)buf, 2);
/*
Set stuff in sector
*/
// Nothing here
}
#endif // !SERVER #endif // !SERVER
//END //END

@ -26,9 +26,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <jmutex.h> #include <jmutex.h>
#include "common_irrlicht.h" #include "common_irrlicht.h"
#include "mapblock.h"
//#include "heightmap.h"
#include "exceptions.h" #include "exceptions.h"
#include <ostream>
class MapBlock;
class Map;
/* /*
This is an Y-wise stack of MapBlocks. This is an Y-wise stack of MapBlocks.
@ -37,18 +39,13 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#define MAPSECTOR_SERVER 0 #define MAPSECTOR_SERVER 0
#define MAPSECTOR_CLIENT 1 #define MAPSECTOR_CLIENT 1
class MapSector: public NodeContainer class MapSector
{ {
public: public:
MapSector(NodeContainer *parent, v2s16 pos); MapSector(Map *parent, v2s16 pos);
virtual ~MapSector(); virtual ~MapSector();
virtual u16 nodeContainerId() const
{
return NODECONTAINER_ID_MAPSECTOR;
}
virtual u32 getId() const = 0; virtual u32 getId() const = 0;
void deleteBlocks(); void deleteBlocks();
@ -59,167 +56,32 @@ public:
} }
MapBlock * getBlockNoCreateNoEx(s16 y); MapBlock * getBlockNoCreateNoEx(s16 y);
MapBlock * getBlockNoCreate(s16 y);
MapBlock * createBlankBlockNoInsert(s16 y); MapBlock * createBlankBlockNoInsert(s16 y);
MapBlock * createBlankBlock(s16 y); MapBlock * createBlankBlock(s16 y);
//MapBlock * getBlock(s16 y, bool generate=true);
void insertBlock(MapBlock *block); void insertBlock(MapBlock *block);
// This is used to remove a dummy from the sector while generating it. void deleteBlock(MapBlock *block);
// Block is only removed from internal container, not deleted.
void removeBlock(MapBlock *block);
/*
This might not be a thread-safe depending on the day.
See the implementation.
*/
void getBlocks(core::list<MapBlock*> &dest); void getBlocks(core::list<MapBlock*> &dest);
/* // Always false at the moment, because sector contains no metadata.
If all nodes in area can be accessed, returns true and
adds all blocks in area to blocks.
If all nodes in area cannot be accessed, returns false.
The implementation of this is quite slow
if blocks==NULL; it is not accessed at all.
*/
bool isValidArea(v3s16 p_min_nodes, v3s16 p_max_nodes,
core::map<s16, MapBlock*> *blocks)
{
core::map<s16, MapBlock*> bs;
v3s16 p_min = getNodeBlockPos(p_min_nodes);
v3s16 p_max = getNodeBlockPos(p_max_nodes);
if(p_min.X != 0 || p_min.Z != 0
|| p_max.X != 0 || p_max.Z != 0)
return false;
v3s16 y;
for(s16 y=p_min.Y; y<=p_max.Y; y++)
{
try{
MapBlock *block = getBlockNoCreate(y);
if(block->isDummy())
return false;
if(blocks!=NULL)
bs[y] = block;
}
catch(InvalidPositionException &e)
{
return false;
}
}
if(blocks!=NULL)
{
for(core::map<s16, MapBlock*>::Iterator i=bs.getIterator();
i.atEnd()==false; i++)
{
MapBlock *block = i.getNode()->getValue();
s16 y = i.getNode()->getKey();
blocks->insert(y, block);
}
}
return true;
}
void getBlocksInArea(v3s16 p_min_nodes, v3s16 p_max_nodes,
core::map<v3s16, MapBlock*> &blocks)
{
v3s16 p_min = getNodeBlockPos(p_min_nodes);
v3s16 p_max = getNodeBlockPos(p_max_nodes);
v3s16 y;
for(s16 y=p_min.Y; y<=p_max.Y; y++)
{
try{
MapBlock *block = getBlockNoCreate(y);
blocks.insert(block->getPos(), block);
}
catch(InvalidPositionException &e)
{
}
}
}
// virtual from NodeContainer
bool isValidPosition(v3s16 p)
{
v3s16 blockpos = getNodeBlockPos(p);
if(blockpos.X != 0 || blockpos.Z != 0)
return false;
MapBlock *blockref;
try{
blockref = getBlockNoCreate(blockpos.Y);
}
catch(InvalidPositionException &e)
{
return false;
}
return true;
}
// virtual from NodeContainer
MapNode getNode(v3s16 p)
{
v3s16 blockpos = getNodeBlockPos(p);
if(blockpos.X != 0 || blockpos.Z != 0)
throw InvalidPositionException
("MapSector only allows Y");
MapBlock * blockref = getBlockNoCreate(blockpos.Y);
v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
return blockref->getNode(relpos);
}
// virtual from NodeContainer
void setNode(v3s16 p, MapNode & n)
{
v3s16 blockpos = getNodeBlockPos(p);
if(blockpos.X != 0 || blockpos.Z != 0)
throw InvalidPositionException
("MapSector only allows Y");
MapBlock * blockref = getBlockNoCreate(blockpos.Y);
v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
blockref->setNode(relpos, n);
}
// DEPRECATED?
virtual f32 getGroundHeight(v2s16 p, bool generate=false)
{
return GROUNDHEIGHT_NOTFOUND_SETVALUE;
}
virtual void setGroundHeight(v2s16 p, f32 y, bool generate=false)
{
}
// When true, sector metadata is changed from the one on disk
// (sector metadata = all but blocks)
// Basically, this should be changed to true in every setter method
bool differs_from_disk; bool differs_from_disk;
protected: protected:
// The pile of MapBlocks // The pile of MapBlocks
core::map<s16, MapBlock*> m_blocks; core::map<s16, MapBlock*> m_blocks;
//JMutex m_blocks_mutex; // For public access functions
NodeContainer *m_parent; Map *m_parent;
// Position on parent (in MapBlock widths) // Position on parent (in MapBlock widths)
v2s16 m_pos; v2s16 m_pos;
// Last-used block is cached here for quicker access.
// Be sure to set this to NULL when the cached block is deleted // Be sure to set this to NULL when the cached block is deleted
MapBlock *m_block_cache; MapBlock *m_block_cache;
s16 m_block_cache_y; s16 m_block_cache_y;
// This is used for protecting m_blocks
JMutex m_mutex;
/* /*
Private methods Private methods
*/ */
@ -230,27 +92,24 @@ protected:
class ServerMapSector : public MapSector class ServerMapSector : public MapSector
{ {
public: public:
ServerMapSector(NodeContainer *parent, v2s16 pos); ServerMapSector(Map *parent, v2s16 pos);
~ServerMapSector(); ~ServerMapSector();
u32 getId() const u32 getId() const
{ {
return MAPSECTOR_SERVER; return MAPSECTOR_SERVER;
} }
// DEPRECATED?
f32 getGroundHeight(v2s16 p, bool generate=false);
void setGroundHeight(v2s16 p, f32 y, bool generate=false);
/* /*
These functions handle metadata. These functions handle metadata.
They do not handle blocks. They do not handle blocks.
*/ */
void serialize(std::ostream &os, u8 version); void serialize(std::ostream &os, u8 version);
static ServerMapSector* deSerialize( static ServerMapSector* deSerialize(
std::istream &is, std::istream &is,
NodeContainer *parent, Map *parent,
v2s16 p2d, v2s16 p2d,
core::map<v2s16, MapSector*> & sectors core::map<v2s16, MapSector*> & sectors
); );
@ -262,7 +121,7 @@ private:
class ClientMapSector : public MapSector class ClientMapSector : public MapSector
{ {
public: public:
ClientMapSector(NodeContainer *parent, v2s16 pos); ClientMapSector(Map *parent, v2s16 pos);
~ClientMapSector(); ~ClientMapSector();
u32 getId() const u32 getId() const
@ -270,16 +129,7 @@ public:
return MAPSECTOR_CLIENT; return MAPSECTOR_CLIENT;
} }
void deSerialize(std::istream &is);
/*s16 getCorner(u16 i)
{
return m_corners[i];
}*/
private: private:
// The ground height of the corners is stored in here
//s16 m_corners[4];
}; };
#endif #endif

@ -21,7 +21,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#define MINERAL_HEADER #define MINERAL_HEADER
#include "inventory.h" #include "inventory.h"
#include "tile.h"
/* /*
Minerals Minerals

@ -238,7 +238,11 @@ double noise3d_param(const NoiseParams &param, double x, double y, double z)
y /= s; y /= s;
z /= s; z /= s;
if(param.type == NOISE_PERLIN) if(param.type == NOISE_CONSTANT_ONE)
{
return 1.0;
}
else if(param.type == NOISE_PERLIN)
{ {
return param.noise_scale*noise3d_perlin(x,y,z, param.seed, return param.noise_scale*noise3d_perlin(x,y,z, param.seed,
param.octaves, param.octaves,

@ -82,10 +82,11 @@ double noise3d_perlin_abs(double x, double y, double z, int seed,
enum NoiseType enum NoiseType
{ {
NOISE_CONSTANT_ONE,
NOISE_PERLIN, NOISE_PERLIN,
NOISE_PERLIN_ABS, NOISE_PERLIN_ABS,
NOISE_PERLIN_CONTOUR, NOISE_PERLIN_CONTOUR,
NOISE_PERLIN_CONTOUR_FLIP_YZ NOISE_PERLIN_CONTOUR_FLIP_YZ,
}; };
struct NoiseParams struct NoiseParams

@ -309,6 +309,8 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d,
v3f oldpos = position; v3f oldpos = position;
v3s16 oldpos_i = floatToInt(oldpos, BS); v3s16 oldpos_i = floatToInt(oldpos, BS);
v3f old_speed = m_speed;
/*std::cout<<"oldpos_i=("<<oldpos_i.X<<","<<oldpos_i.Y<<"," /*std::cout<<"oldpos_i=("<<oldpos_i.X<<","<<oldpos_i.Y<<","
<<oldpos_i.Z<<")"<<std::endl;*/ <<oldpos_i.Z<<")"<<std::endl;*/
@ -405,8 +407,23 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d,
if(position.Y < min_y) if(position.Y < min_y)
{ {
position.Y = min_y; position.Y = min_y;
//v3f old_speed = m_speed;
if(m_speed.Y < 0) if(m_speed.Y < 0)
m_speed.Y = 0; m_speed.Y = 0;
/*if(collision_info)
{
// Report fall collision
if(old_speed.Y < m_speed.Y - 0.1)
{
CollisionInfo info;
info.t = COLLISION_FALL;
info.speed = m_speed.Y - old_speed.Y;
collision_info->push_back(info);
}
}*/
} }
} }
@ -557,13 +574,13 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d,
*/ */
if(other_axes_overlap && main_axis_collides) if(other_axes_overlap && main_axis_collides)
{ {
v3f old_speed = m_speed; //v3f old_speed = m_speed;
m_speed -= m_speed.dotProduct(dirs[i]) * dirs[i]; m_speed -= m_speed.dotProduct(dirs[i]) * dirs[i];
position -= position.dotProduct(dirs[i]) * dirs[i]; position -= position.dotProduct(dirs[i]) * dirs[i];
position += oldpos.dotProduct(dirs[i]) * dirs[i]; position += oldpos.dotProduct(dirs[i]) * dirs[i];
if(collision_info) /*if(collision_info)
{ {
// Report fall collision // Report fall collision
if(old_speed.Y < m_speed.Y - 0.1) if(old_speed.Y < m_speed.Y - 0.1)
@ -573,7 +590,7 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d,
info.speed = m_speed.Y - old_speed.Y; info.speed = m_speed.Y - old_speed.Y;
collision_info->push_back(info); collision_info->push_back(info);
} }
} }*/
} }
} }
@ -656,6 +673,21 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d,
Set new position Set new position
*/ */
setPosition(position); setPosition(position);
/*
Report collisions
*/
if(collision_info)
{
// Report fall collision
if(old_speed.Y < m_speed.Y - 0.1)
{
CollisionInfo info;
info.t = COLLISION_FALL;
info.speed = m_speed.Y - old_speed.Y;
collision_info->push_back(info);
}
}
} }
void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d) void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d)

@ -53,12 +53,13 @@ with this program; if not, write to the Free Software Foundation, Inc.,
15: StaticObjects 15: StaticObjects
16: larger maximum size of node metadata, and compression 16: larger maximum size of node metadata, and compression
17: MapBlocks contain timestamp 17: MapBlocks contain timestamp
18: sqlite/new generator/whatever 18: new generator (not really necessary, but it's there)
19: new content type handling
*/ */
// This represents an uninitialized or invalid format // This represents an uninitialized or invalid format
#define SER_FMT_VER_INVALID 255 #define SER_FMT_VER_INVALID 255
// Highest supported serialization version // Highest supported serialization version
#define SER_FMT_VER_HIGHEST 18 #define SER_FMT_VER_HIGHEST 19
// Lowest supported serialization version // Lowest supported serialization version
#define SER_FMT_VER_LOWEST 0 #define SER_FMT_VER_LOWEST 0

@ -34,6 +34,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "content_mapnode.h" #include "content_mapnode.h"
#include "content_craft.h" #include "content_craft.h"
#include "content_nodemeta.h" #include "content_nodemeta.h"
#include "mapblock.h"
#include "serverobject.h"
#define BLOCK_EMERGE_FLAG_FROMDISK (1<<0) #define BLOCK_EMERGE_FLAG_FROMDISK (1<<0)
@ -600,6 +602,9 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
bool block_is_invalid = false; bool block_is_invalid = false;
if(block != NULL) if(block != NULL)
{ {
// Reset usage timer, this block will be of use in the future.
block->resetUsageTimer();
// Block is dummy if data doesn't exist. // Block is dummy if data doesn't exist.
// It means it has been not found from disk and not generated // It means it has been not found from disk and not generated
if(block->isDummy()) if(block->isDummy())
@ -1295,12 +1300,21 @@ void Server::AsyncRunStep()
} }
{ {
// Step environment
// This also runs Map's timers
JMutexAutoLock lock(m_env_mutex); JMutexAutoLock lock(m_env_mutex);
// Step environment
ScopeProfiler sp(&g_profiler, "Server: environment step"); ScopeProfiler sp(&g_profiler, "Server: environment step");
m_env.step(dtime); m_env.step(dtime);
} }
const float map_timer_and_unload_dtime = 5.15;
if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
{
JMutexAutoLock lock(m_env_mutex);
// Run Map's timers and unload unused data
ScopeProfiler sp(&g_profiler, "Server: map timer and unload");
m_env.getMap().timerUpdate(map_timer_and_unload_dtime,
g_settings.getFloat("server_unload_unused_data_timeout"));
}
/* /*
Do background stuff Do background stuff
@ -1656,9 +1670,22 @@ void Server::AsyncRunStep()
*/ */
{ {
// Don't send too many at a time // Don't send too many at a time
u32 count = 0; //u32 count = 0;
// Single change sending is disabled if queue size is not small
bool disable_single_change_sending = false;
if(m_unsent_map_edit_queue.size() >= 4)
disable_single_change_sending = true;
bool got_any_events = false;
// We'll log the amount of each
Profiler prof;
while(m_unsent_map_edit_queue.size() != 0) while(m_unsent_map_edit_queue.size() != 0)
{ {
got_any_events = true;
MapEditEvent* event = m_unsent_map_edit_queue.pop_front(); MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
// Players far away from the change are stored here. // Players far away from the change are stored here.
@ -1668,28 +1695,41 @@ void Server::AsyncRunStep()
if(event->type == MEET_ADDNODE) if(event->type == MEET_ADDNODE)
{ {
dstream<<"Server: MEET_ADDNODE"<<std::endl; //dstream<<"Server: MEET_ADDNODE"<<std::endl;
sendAddNode(event->p, event->n, event->already_known_by_peer, prof.add("MEET_ADDNODE", 1);
&far_players, 30); if(disable_single_change_sending)
sendAddNode(event->p, event->n, event->already_known_by_peer,
&far_players, 5);
else
sendAddNode(event->p, event->n, event->already_known_by_peer,
&far_players, 30);
} }
else if(event->type == MEET_REMOVENODE) else if(event->type == MEET_REMOVENODE)
{ {
dstream<<"Server: MEET_REMOVENODE"<<std::endl; //dstream<<"Server: MEET_REMOVENODE"<<std::endl;
sendRemoveNode(event->p, event->already_known_by_peer, prof.add("MEET_REMOVENODE", 1);
&far_players, 30); if(disable_single_change_sending)
sendRemoveNode(event->p, event->already_known_by_peer,
&far_players, 5);
else
sendRemoveNode(event->p, event->already_known_by_peer,
&far_players, 30);
} }
else if(event->type == MEET_BLOCK_NODE_METADATA_CHANGED) else if(event->type == MEET_BLOCK_NODE_METADATA_CHANGED)
{ {
dstream<<"Server: MEET_BLOCK_NODE_METADATA_CHANGED"<<std::endl; dstream<<"Server: MEET_BLOCK_NODE_METADATA_CHANGED"<<std::endl;
prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
setBlockNotSent(event->p); setBlockNotSent(event->p);
} }
else if(event->type == MEET_OTHER) else if(event->type == MEET_OTHER)
{ {
prof.add("MEET_OTHER", 1);
dstream<<"WARNING: Server: MEET_OTHER not implemented" dstream<<"WARNING: Server: MEET_OTHER not implemented"
<<std::endl; <<std::endl;
} }
else else
{ {
prof.add("unknown", 1);
dstream<<"WARNING: Server: Unknown MapEditEvent " dstream<<"WARNING: Server: Unknown MapEditEvent "
<<((u32)event->type)<<std::endl; <<((u32)event->type)<<std::endl;
} }
@ -1697,32 +1737,45 @@ void Server::AsyncRunStep()
/* /*
Set blocks not sent to far players Set blocks not sent to far players
*/ */
core::map<v3s16, MapBlock*> modified_blocks2; if(far_players.size() > 0)
for(core::map<v3s16, bool>::Iterator
i = event->modified_blocks.getIterator();
i.atEnd()==false; i++)
{ {
v3s16 p = i.getNode()->getKey(); // Convert list format to that wanted by SetBlocksNotSent
modified_blocks2.insert(p, m_env.getMap().getBlockNoCreateNoEx(p)); core::map<v3s16, MapBlock*> modified_blocks2;
} for(core::map<v3s16, bool>::Iterator
for(core::list<u16>::Iterator i = event->modified_blocks.getIterator();
i = far_players.begin(); i.atEnd()==false; i++)
i != far_players.end(); i++) {
{ v3s16 p = i.getNode()->getKey();
u16 peer_id = *i; modified_blocks2.insert(p,
RemoteClient *client = getClient(peer_id); m_env.getMap().getBlockNoCreateNoEx(p));
if(client==NULL) }
continue; // Set blocks not sent
client->SetBlocksNotSent(modified_blocks2); for(core::list<u16>::Iterator
i = far_players.begin();
i != far_players.end(); i++)
{
u16 peer_id = *i;
RemoteClient *client = getClient(peer_id);
if(client==NULL)
continue;
client->SetBlocksNotSent(modified_blocks2);
}
} }
delete event; delete event;
// Don't send too many at a time /*// Don't send too many at a time
count++; count++;
if(count >= 1 && m_unsent_map_edit_queue.size() < 100) if(count >= 1 && m_unsent_map_edit_queue.size() < 100)
break; break;*/
} }
if(got_any_events)
{
dstream<<"Server: MapEditEvents:"<<std::endl;
prof.print(dstream);
}
} }
/* /*
@ -1745,39 +1798,6 @@ void Server::AsyncRunStep()
} }
} }
/*
Step node metadata
TODO: Move to ServerEnvironment and utilize active block stuff
*/
/*{
//TimeTaker timer("Step node metadata");
JMutexAutoLock envlock(m_env_mutex);
JMutexAutoLock conlock(m_con_mutex);
ScopeProfiler sp(&g_profiler, "Server: stepping node metadata");
core::map<v3s16, MapBlock*> changed_blocks;
m_env.getMap().nodeMetadataStep(dtime, changed_blocks);
// Use setBlockNotSent
for(core::map<v3s16, MapBlock*>::Iterator
i = changed_blocks.getIterator();
i.atEnd() == false; i++)
{
MapBlock *block = i.getNode()->getValue();
for(core::map<u16, RemoteClient*>::Iterator
i = m_clients.getIterator();
i.atEnd()==false; i++)
{
RemoteClient *client = i.getNode()->getValue();
client->SetBlockNotSent(block->getPos());
}
}
}*/
/* /*
Trigger emergethread (it somehow gets to a non-triggered but Trigger emergethread (it somehow gets to a non-triggered but
bysy state sometimes) bysy state sometimes)
@ -1809,26 +1829,29 @@ void Server::AsyncRunStep()
// Map // Map
JMutexAutoLock lock(m_env_mutex); JMutexAutoLock lock(m_env_mutex);
if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == true)
/*// Unload unused data (delete from memory)
m_env.getMap().unloadUnusedData(
g_settings.getFloat("server_unload_unused_sectors_timeout"));
*/
/*u32 deleted_count = m_env.getMap().unloadUnusedData(
g_settings.getFloat("server_unload_unused_sectors_timeout"));
*/
// Save only changed parts
m_env.getMap().save(true);
/*if(deleted_count > 0)
{ {
// Save only changed parts dout_server<<"Server: Unloaded "<<deleted_count
m_env.getMap().save(true); <<" blocks from memory"<<std::endl;
}*/
// Delete unused sectors // Save players
u32 deleted_count = m_env.getMap().unloadUnusedData( m_env.serializePlayers(m_mapsavedir);
g_settings.getFloat("server_unload_unused_sectors_timeout"));
if(deleted_count > 0) // Save environment metadata
{ m_env.saveMeta(m_mapsavedir);
dout_server<<"Server: Unloaded "<<deleted_count
<<" sectors from memory"<<std::endl;
}
// Save players
m_env.serializePlayers(m_mapsavedir);
// Save environment metadata
m_env.saveMeta(m_mapsavedir);
}
} }
} }
} }
@ -2392,8 +2415,12 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
toolname = titem->getToolName(); toolname = titem->getToolName();
} }
} }
v3f playerpos = player->getPosition();
v3f objpos = obj->getBasePosition();
v3f dir = (objpos - playerpos).normalize();
u16 wear = obj->punch(toolname); u16 wear = obj->punch(toolname, dir);
if(titem) if(titem)
{ {
@ -2710,9 +2737,31 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
MaterialItem *mitem = (MaterialItem*)item; MaterialItem *mitem = (MaterialItem*)item;
MapNode n; MapNode n;
n.d = mitem->getMaterial(); n.d = mitem->getMaterial();
// Calculate direction for wall mounted stuff
if(content_features(n.d).wall_mounted) if(content_features(n.d).wall_mounted)
n.dir = packDir(p_under - p_over); n.dir = packDir(p_under - p_over);
// Calculate the direction for furnaces and chests and stuff
if(content_features(n.d).param_type == CPT_FACEDIR_SIMPLE)
{
v3f playerpos = player->getPosition();
v3f blockpos = intToFloat(p_over, BS) - playerpos;
blockpos = blockpos.normalize();
n.param1 = 0;
if (fabs(blockpos.X) > fabs(blockpos.Z)) {
if (blockpos.X < 0)
n.param1 = 3;
else
n.param1 = 1;
} else {
if (blockpos.Z < 0)
n.param1 = 2;
else
n.param1 = 0;
}
}
/* /*
Send to all close-by players Send to all close-by players
*/ */
@ -3286,7 +3335,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
void Server::onMapEditEvent(MapEditEvent *event) void Server::onMapEditEvent(MapEditEvent *event)
{ {
dstream<<"Server::onMapEditEvent()"<<std::endl; //dstream<<"Server::onMapEditEvent()"<<std::endl;
if(m_ignore_map_edit_events) if(m_ignore_map_edit_events)
return; return;
MapEditEvent *e = event->clone(); MapEditEvent *e = event->clone();

@ -534,6 +534,7 @@ private:
float m_objectdata_timer; float m_objectdata_timer;
float m_emergethread_trigger_timer; float m_emergethread_trigger_timer;
float m_savemap_timer; float m_savemap_timer;
IntervalLimiter m_map_timer_and_unload_interval;
// NOTE: If connection and environment are both to be locked, // NOTE: If connection and environment are both to be locked,
// environment shall be locked first. // environment shall be locked first.

@ -19,9 +19,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "serverobject.h" #include "serverobject.h"
#include <fstream> #include <fstream>
#include "environment.h"
#include "inventory.h" #include "inventory.h"
#include "collision.h"
core::map<u16, ServerActiveObject::Factory> ServerActiveObject::m_types; core::map<u16, ServerActiveObject::Factory> ServerActiveObject::m_types;
@ -71,600 +69,4 @@ void ServerActiveObject::registerType(u16 type, Factory f)
} }
/*
TestSAO
*/
// Prototype
TestSAO proto_TestSAO(NULL, 0, v3f(0,0,0));
TestSAO::TestSAO(ServerEnvironment *env, u16 id, v3f pos):
ServerActiveObject(env, id, pos),
m_timer1(0),
m_age(0)
{
ServerActiveObject::registerType(getType(), create);
}
ServerActiveObject* TestSAO::create(ServerEnvironment *env, u16 id, v3f pos,
const std::string &data)
{
return new TestSAO(env, id, pos);
}
void TestSAO::step(float dtime, Queue<ActiveObjectMessage> &messages,
bool send_recommended)
{
m_age += dtime;
if(m_age > 10)
{
m_removed = true;
return;
}
m_base_position.Y += dtime * BS * 2;
if(m_base_position.Y > 8*BS)
m_base_position.Y = 2*BS;
if(send_recommended == false)
return;
m_timer1 -= dtime;
if(m_timer1 < 0.0)
{
m_timer1 += 0.125;
//dstream<<"TestSAO: id="<<getId()<<" sending data"<<std::endl;
std::string data;
data += itos(0); // 0 = position
data += " ";
data += itos(m_base_position.X);
data += " ";
data += itos(m_base_position.Y);
data += " ";
data += itos(m_base_position.Z);
ActiveObjectMessage aom(getId(), false, data);
messages.push_back(aom);
}
}
/*
ItemSAO
*/
// Prototype
ItemSAO proto_ItemSAO(NULL, 0, v3f(0,0,0), "");
ItemSAO::ItemSAO(ServerEnvironment *env, u16 id, v3f pos,
const std::string inventorystring):
ServerActiveObject(env, id, pos),
m_inventorystring(inventorystring),
m_speed_f(0,0,0),
m_last_sent_position(0,0,0)
{
ServerActiveObject::registerType(getType(), create);
}
ServerActiveObject* ItemSAO::create(ServerEnvironment *env, u16 id, v3f pos,
const std::string &data)
{
std::istringstream is(data, std::ios::binary);
char buf[1];
// read version
is.read(buf, 1);
u8 version = buf[0];
// check if version is supported
if(version != 0)
return NULL;
std::string inventorystring = deSerializeString(is);
dstream<<"ItemSAO::create(): Creating item \""
<<inventorystring<<"\""<<std::endl;
return new ItemSAO(env, id, pos, inventorystring);
}
void ItemSAO::step(float dtime, Queue<ActiveObjectMessage> &messages,
bool send_recommended)
{
assert(m_env);
const float interval = 0.2;
if(m_move_interval.step(dtime, interval)==false)
return;
dtime = interval;
core::aabbox3d<f32> box(-BS/3.,0.0,-BS/3., BS/3.,BS*2./3.,BS/3.);
collisionMoveResult moveresult;
// Apply gravity
m_speed_f += v3f(0, -dtime*9.81*BS, 0);
// Maximum movement without glitches
f32 pos_max_d = BS*0.25;
// Limit speed
if(m_speed_f.getLength()*dtime > pos_max_d)
m_speed_f *= pos_max_d / (m_speed_f.getLength()*dtime);
v3f pos_f = getBasePosition();
v3f pos_f_old = pos_f;
moveresult = collisionMoveSimple(&m_env->getMap(), pos_max_d,
box, dtime, pos_f, m_speed_f);
if(send_recommended == false)
return;
if(pos_f.getDistanceFrom(m_last_sent_position) > 0.05*BS)
{
setBasePosition(pos_f);
m_last_sent_position = pos_f;
std::ostringstream os(std::ios::binary);
char buf[6];
// command (0 = update position)
buf[0] = 0;
os.write(buf, 1);
// pos
writeS32((u8*)buf, m_base_position.X*1000);
os.write(buf, 4);
writeS32((u8*)buf, m_base_position.Y*1000);
os.write(buf, 4);
writeS32((u8*)buf, m_base_position.Z*1000);
os.write(buf, 4);
// create message and add to list
ActiveObjectMessage aom(getId(), false, os.str());
messages.push_back(aom);
}
}
std::string ItemSAO::getClientInitializationData()
{
std::ostringstream os(std::ios::binary);
char buf[6];
// version
buf[0] = 0;
os.write(buf, 1);
// pos
writeS32((u8*)buf, m_base_position.X*1000);
os.write(buf, 4);
writeS32((u8*)buf, m_base_position.Y*1000);
os.write(buf, 4);
writeS32((u8*)buf, m_base_position.Z*1000);
os.write(buf, 4);
// inventorystring
os<<serializeString(m_inventorystring);
return os.str();
}
std::string ItemSAO::getStaticData()
{
dstream<<__FUNCTION_NAME<<std::endl;
std::ostringstream os(std::ios::binary);
char buf[1];
// version
buf[0] = 0;
os.write(buf, 1);
// inventorystring
os<<serializeString(m_inventorystring);
return os.str();
}
InventoryItem * ItemSAO::createInventoryItem()
{
try{
std::istringstream is(m_inventorystring, std::ios_base::binary);
InventoryItem *item = InventoryItem::deSerialize(is);
dstream<<__FUNCTION_NAME<<": m_inventorystring=\""
<<m_inventorystring<<"\" -> item="<<item
<<std::endl;
return item;
}
catch(SerializationError &e)
{
dstream<<__FUNCTION_NAME<<": serialization error: "
<<"m_inventorystring=\""<<m_inventorystring<<"\""<<std::endl;
return NULL;
}
}
/*
RatSAO
*/
// Prototype
RatSAO proto_RatSAO(NULL, 0, v3f(0,0,0));
RatSAO::RatSAO(ServerEnvironment *env, u16 id, v3f pos):
ServerActiveObject(env, id, pos),
m_is_active(false),
m_speed_f(0,0,0)
{
ServerActiveObject::registerType(getType(), create);
m_oldpos = v3f(0,0,0);
m_last_sent_position = v3f(0,0,0);
m_yaw = 0;
m_counter1 = 0;
m_counter2 = 0;
m_age = 0;
m_touching_ground = false;
}
ServerActiveObject* RatSAO::create(ServerEnvironment *env, u16 id, v3f pos,
const std::string &data)
{
std::istringstream is(data, std::ios::binary);
char buf[1];
// read version
is.read(buf, 1);
u8 version = buf[0];
// check if version is supported
if(version != 0)
return NULL;
return new RatSAO(env, id, pos);
}
void RatSAO::step(float dtime, Queue<ActiveObjectMessage> &messages,
bool send_recommended)
{
assert(m_env);
if(m_is_active == false)
{
if(m_inactive_interval.step(dtime, 0.5)==false)
return;
}
/*
The AI
*/
/*m_age += dtime;
if(m_age > 60)
{
// Die
m_removed = true;
return;
}*/
// Apply gravity
m_speed_f.Y -= dtime*9.81*BS;
/*
Move around if some player is close
*/
bool player_is_close = false;
// Check connected players
core::list<Player*> players = m_env->getPlayers(true);
core::list<Player*>::Iterator i;
for(i = players.begin();
i != players.end(); i++)
{
Player *player = *i;
v3f playerpos = player->getPosition();
if(m_base_position.getDistanceFrom(playerpos) < BS*10.0)
{
player_is_close = true;
break;
}
}
m_is_active = player_is_close;
if(player_is_close == false)
{
m_speed_f.X = 0;
m_speed_f.Z = 0;
}
else
{
// Move around
v3f dir(cos(m_yaw/180*PI),0,sin(m_yaw/180*PI));
f32 speed = 2*BS;
m_speed_f.X = speed * dir.X;
m_speed_f.Z = speed * dir.Z;
if(m_touching_ground && (m_oldpos - m_base_position).getLength()
< dtime*speed/2)
{
m_counter1 -= dtime;
if(m_counter1 < 0.0)
{
m_counter1 += 1.0;
m_speed_f.Y = 5.0*BS;
}
}
{
m_counter2 -= dtime;
if(m_counter2 < 0.0)
{
m_counter2 += (float)(myrand()%100)/100*3.0;
m_yaw += ((float)(myrand()%200)-100)/100*180;
m_yaw = wrapDegrees(m_yaw);
}
}
}
m_oldpos = m_base_position;
/*
Move it, with collision detection
*/
core::aabbox3d<f32> box(-BS/3.,0.0,-BS/3., BS/3.,BS*2./3.,BS/3.);
collisionMoveResult moveresult;
// Maximum movement without glitches
f32 pos_max_d = BS*0.25;
// Limit speed
if(m_speed_f.getLength()*dtime > pos_max_d)
m_speed_f *= pos_max_d / (m_speed_f.getLength()*dtime);
v3f pos_f = getBasePosition();
v3f pos_f_old = pos_f;
moveresult = collisionMoveSimple(&m_env->getMap(), pos_max_d,
box, dtime, pos_f, m_speed_f);
m_touching_ground = moveresult.touching_ground;
setBasePosition(pos_f);
if(send_recommended == false)
return;
if(pos_f.getDistanceFrom(m_last_sent_position) > 0.05*BS)
{
m_last_sent_position = pos_f;
std::ostringstream os(std::ios::binary);
// command (0 = update position)
writeU8(os, 0);
// pos
writeV3F1000(os, m_base_position);
// yaw
writeF1000(os, m_yaw);
// create message and add to list
ActiveObjectMessage aom(getId(), false, os.str());
messages.push_back(aom);
}
}
std::string RatSAO::getClientInitializationData()
{
std::ostringstream os(std::ios::binary);
// version
writeU8(os, 0);
// pos
writeV3F1000(os, m_base_position);
return os.str();
}
std::string RatSAO::getStaticData()
{
//dstream<<__FUNCTION_NAME<<std::endl;
std::ostringstream os(std::ios::binary);
// version
writeU8(os, 0);
return os.str();
}
InventoryItem* RatSAO::createPickedUpItem()
{
std::istringstream is("CraftItem rat 1", std::ios_base::binary);
InventoryItem *item = InventoryItem::deSerialize(is);
return item;
}
/*
Oerkki1SAO
*/
// Prototype
Oerkki1SAO proto_Oerkki1SAO(NULL, 0, v3f(0,0,0));
Oerkki1SAO::Oerkki1SAO(ServerEnvironment *env, u16 id, v3f pos):
ServerActiveObject(env, id, pos),
m_is_active(false),
m_speed_f(0,0,0)
{
ServerActiveObject::registerType(getType(), create);
m_oldpos = v3f(0,0,0);
m_last_sent_position = v3f(0,0,0);
m_yaw = 0;
m_counter1 = 0;
m_counter2 = 0;
m_age = 0;
m_touching_ground = false;
m_hp = 20;
}
ServerActiveObject* Oerkki1SAO::create(ServerEnvironment *env, u16 id, v3f pos,
const std::string &data)
{
std::istringstream is(data, std::ios::binary);
// read version
u8 version = readU8(is);
// read hp
u8 hp = readU8(is);
// check if version is supported
if(version != 0)
return NULL;
Oerkki1SAO *o = new Oerkki1SAO(env, id, pos);
o->m_hp = hp;
return o;
}
void Oerkki1SAO::step(float dtime, Queue<ActiveObjectMessage> &messages,
bool send_recommended)
{
assert(m_env);
if(m_is_active == false)
{
if(m_inactive_interval.step(dtime, 0.5)==false)
return;
}
/*
The AI
*/
m_age += dtime;
if(m_age > 120)
{
// Die
m_removed = true;
return;
}
// Apply gravity
m_speed_f.Y -= dtime*9.81*BS;
/*
Move around if some player is close
*/
bool player_is_close = false;
v3f near_player_pos;
// Check connected players
core::list<Player*> players = m_env->getPlayers(true);
core::list<Player*>::Iterator i;
for(i = players.begin();
i != players.end(); i++)
{
Player *player = *i;
v3f playerpos = player->getPosition();
if(m_base_position.getDistanceFrom(playerpos) < BS*15.0)
{
player_is_close = true;
near_player_pos = playerpos;
break;
}
}
m_is_active = player_is_close;
if(player_is_close == false)
{
m_speed_f.X = 0;
m_speed_f.Z = 0;
}
else
{
// Move around
v3f ndir = near_player_pos - m_base_position;
ndir.Y = 0;
ndir /= ndir.getLength();
f32 nyaw = 180./PI*atan2(ndir.Z,ndir.X);
if(nyaw < m_yaw - 180)
nyaw += 360;
else if(nyaw > m_yaw + 180)
nyaw -= 360;
m_yaw = 0.95*m_yaw + 0.05*nyaw;
m_yaw = wrapDegrees(m_yaw);
v3f dir(cos(m_yaw/180*PI),0,sin(m_yaw/180*PI));
f32 speed = 2*BS;
m_speed_f.X = speed * dir.X;
m_speed_f.Z = speed * dir.Z;
if(m_touching_ground && (m_oldpos - m_base_position).getLength()
< dtime*speed/2)
{
m_counter1 -= dtime;
if(m_counter1 < 0.0)
{
m_counter1 += 1.0;
// Jump
m_speed_f.Y = 5.0*BS;
}
}
{
m_counter2 -= dtime;
if(m_counter2 < 0.0)
{
m_counter2 += (float)(myrand()%100)/100*3.0;
//m_yaw += ((float)(myrand()%200)-100)/100*180;
m_yaw += ((float)(myrand()%200)-100)/100*90;
m_yaw = wrapDegrees(m_yaw);
}
}
}
m_oldpos = m_base_position;
/*
Move it, with collision detection
*/
core::aabbox3d<f32> box(-BS/3.,0.0,-BS/3., BS/3.,BS*5./3.,BS/3.);
collisionMoveResult moveresult;
// Maximum movement without glitches
f32 pos_max_d = BS*0.25;
// Limit speed
if(m_speed_f.getLength()*dtime > pos_max_d)
m_speed_f *= pos_max_d / (m_speed_f.getLength()*dtime);
v3f pos_f = getBasePosition();
v3f pos_f_old = pos_f;
moveresult = collisionMoveSimple(&m_env->getMap(), pos_max_d,
box, dtime, pos_f, m_speed_f);
m_touching_ground = moveresult.touching_ground;
setBasePosition(pos_f);
if(send_recommended == false)
return;
if(pos_f.getDistanceFrom(m_last_sent_position) > 0.05*BS)
{
m_last_sent_position = pos_f;
std::ostringstream os(std::ios::binary);
// command (0 = update position)
writeU8(os, 0);
// pos
writeV3F1000(os, m_base_position);
// yaw
writeF1000(os, m_yaw);
// create message and add to list
ActiveObjectMessage aom(getId(), false, os.str());
messages.push_back(aom);
}
}
std::string Oerkki1SAO::getClientInitializationData()
{
std::ostringstream os(std::ios::binary);
// version
writeU8(os, 0);
// pos
writeV3F1000(os, m_base_position);
return os.str();
}
std::string Oerkki1SAO::getStaticData()
{
//dstream<<__FUNCTION_NAME<<std::endl;
std::ostringstream os(std::ios::binary);
// version
writeU8(os, 0);
// hp
writeU8(os, m_hp);
return os.str();
}
u16 Oerkki1SAO::punch(const std::string &toolname)
{
u16 amount = 5;
if(amount < m_hp)
{
m_hp -= amount;
}
else
{
// Die
m_removed = true;
}
return 65536/100;
}

@ -78,8 +78,7 @@ public:
same time so that the data can be combined in a single same time so that the data can be combined in a single
packet. packet.
*/ */
virtual void step(float dtime, Queue<ActiveObjectMessage> &messages, virtual void step(float dtime, bool send_recommended){}
bool send_recommended){}
/* /*
The return value of this is passed to the client-side object The return value of this is passed to the client-side object
@ -104,7 +103,8 @@ public:
If the object doesn't return an item, this will be called. If the object doesn't return an item, this will be called.
Return value is tool wear. Return value is tool wear.
*/ */
virtual u16 punch(const std::string &toolname){return 0;} virtual u16 punch(const std::string &toolname, v3f dir)
{return 0;}
/* /*
Number of players which know about this object. Object won't be Number of players which know about this object. Object won't be
@ -144,6 +144,11 @@ public:
*/ */
v3s16 m_static_block; v3s16 m_static_block;
/*
Queue of messages to be sent to the client
*/
Queue<ActiveObjectMessage> m_messages_out;
protected: protected:
// Used for creating objects based on type // Used for creating objects based on type
typedef ServerActiveObject* (*Factory) typedef ServerActiveObject* (*Factory)
@ -159,96 +164,5 @@ private:
static core::map<u16, Factory> m_types; static core::map<u16, Factory> m_types;
}; };
class TestSAO : public ServerActiveObject
{
public:
TestSAO(ServerEnvironment *env, u16 id, v3f pos);
u8 getType() const
{return ACTIVEOBJECT_TYPE_TEST;}
static ServerActiveObject* create(ServerEnvironment *env, u16 id, v3f pos,
const std::string &data);
void step(float dtime, Queue<ActiveObjectMessage> &messages,
bool send_recommended);
private:
float m_timer1;
float m_age;
};
class ItemSAO : public ServerActiveObject
{
public:
ItemSAO(ServerEnvironment *env, u16 id, v3f pos,
const std::string inventorystring);
u8 getType() const
{return ACTIVEOBJECT_TYPE_ITEM;}
static ServerActiveObject* create(ServerEnvironment *env, u16 id, v3f pos,
const std::string &data);
void step(float dtime, Queue<ActiveObjectMessage> &messages,
bool send_recommended);
std::string getClientInitializationData();
std::string getStaticData();
InventoryItem* createInventoryItem();
InventoryItem* createPickedUpItem(){return createInventoryItem();}
private:
std::string m_inventorystring;
v3f m_speed_f;
v3f m_last_sent_position;
IntervalLimiter m_move_interval;
};
class RatSAO : public ServerActiveObject
{
public:
RatSAO(ServerEnvironment *env, u16 id, v3f pos);
u8 getType() const
{return ACTIVEOBJECT_TYPE_RAT;}
static ServerActiveObject* create(ServerEnvironment *env, u16 id, v3f pos,
const std::string &data);
void step(float dtime, Queue<ActiveObjectMessage> &messages,
bool send_recommended);
std::string getClientInitializationData();
std::string getStaticData();
InventoryItem* createPickedUpItem();
private:
bool m_is_active;
IntervalLimiter m_inactive_interval;
v3f m_speed_f;
v3f m_oldpos;
v3f m_last_sent_position;
float m_yaw;
float m_counter1;
float m_counter2;
float m_age;
bool m_touching_ground;
};
class Oerkki1SAO : public ServerActiveObject
{
public:
Oerkki1SAO(ServerEnvironment *env, u16 id, v3f pos);
u8 getType() const
{return ACTIVEOBJECT_TYPE_OERKKI1;}
static ServerActiveObject* create(ServerEnvironment *env, u16 id, v3f pos,
const std::string &data);
void step(float dtime, Queue<ActiveObjectMessage> &messages,
bool send_recommended);
std::string getClientInitializationData();
std::string getStaticData();
InventoryItem* createPickedUpItem(){return NULL;}
u16 punch(const std::string &toolname);
private:
bool m_is_active;
IntervalLimiter m_inactive_interval;
v3f m_speed_f;
v3f m_oldpos;
v3f m_last_sent_position;
float m_yaw;
float m_counter1;
float m_counter2;
float m_age;
bool m_touching_ground;
u8 m_hp;
};
#endif #endif

@ -31,6 +31,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <sstream> #include <sstream>
#include "porting.h" #include "porting.h"
#include "content_mapnode.h" #include "content_mapnode.h"
#include "mapsector.h"
/* /*
Asserts that the exception occurs Asserts that the exception occurs
@ -339,6 +340,12 @@ struct TestVoxelManipulator
} }
}; };
/*
NOTE: These tests became non-working then NodeContainer was removed.
These should be redone, utilizing some kind of a virtual
interface for Map (IMap would be fine).
*/
#if 0
struct TestMapBlock struct TestMapBlock
{ {
class TC : public NodeContainer class TC : public NodeContainer
@ -641,13 +648,13 @@ struct TestMapSector
// Create one with no heightmaps // Create one with no heightmaps
ServerMapSector sector(&parent, v2s16(1,1)); ServerMapSector sector(&parent, v2s16(1,1));
EXCEPTION_CHECK(InvalidPositionException, sector.getBlockNoCreate(0)); assert(sector.getBlockNoCreateNoEx(0) == 0);
EXCEPTION_CHECK(InvalidPositionException, sector.getBlockNoCreate(1)); assert(sector.getBlockNoCreateNoEx(1) == 0);
MapBlock * bref = sector.createBlankBlock(-2); MapBlock * bref = sector.createBlankBlock(-2);
EXCEPTION_CHECK(InvalidPositionException, sector.getBlockNoCreate(0)); assert(sector.getBlockNoCreateNoEx(0) == 0);
assert(sector.getBlockNoCreate(-2) == bref); assert(sector.getBlockNoCreateNoEx(-2) == bref);
//TODO: Check for AlreadyExistsException //TODO: Check for AlreadyExistsException
@ -662,6 +669,7 @@ struct TestMapSector
} }
}; };
#endif
struct TestSocket struct TestSocket
{ {
@ -1028,8 +1036,8 @@ void run_tests()
TEST(TestCompress); TEST(TestCompress);
TEST(TestMapNode); TEST(TestMapNode);
TEST(TestVoxelManipulator); TEST(TestVoxelManipulator);
TEST(TestMapBlock); //TEST(TestMapBlock);
TEST(TestMapSector); //TEST(TestMapSector);
if(INTERNET_SIMULATOR == false){ if(INTERNET_SIMULATOR == false){
TEST(TestSocket); TEST(TestSocket);
dout_con<<"=== BEGIN RUNNING UNIT TESTS FOR CONNECTION ==="<<std::endl; dout_con<<"=== BEGIN RUNNING UNIT TESTS FOR CONNECTION ==="<<std::endl;