# Set to true to enable creative mode (unlimited inventory)
#creative_mode = false
+#enable_damage = false
+
# Player and object positions are sent at intervals specified by this
#objectdata_inverval = 0.2
#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
m_connection_reinit_timer = 0.0;
m_avg_rtt_timer = 0.0;
m_playerpos_send_timer = 0.0;
+ m_ignore_damage_timer = 0.0;
//m_env_mutex.Init();
//m_con_mutex.Init();
if(dtime > 2.0)
dtime = 2.0;
+ if(m_ignore_damage_timer > dtime)
+ m_ignore_damage_timer -= dtime;
+ else
+ m_ignore_damage_timer = 0.0;
//dstream<<"Client steps "<<dtime<<std::endl;
Do stuff if connected
*/
+ /*
+ Handle environment
+ */
{
// 0ms
//JMutexAutoLock lock(m_env_mutex); //bulk comment-out
{
}
}
- }
+ /*
+ Get events
+ */
+ for(;;)
+ {
+ ClientEnvEvent event = m_env.getClientEvent();
+ if(event.type == CEE_NONE)
+ {
+ break;
+ }
+ else if(event.type == CEE_PLAYER_DAMAGE)
+ {
+ if(m_ignore_damage_timer <= 0)
+ {
+ u8 damage = event.player_damage.amount;
+ sendDamage(damage);
+
+ // Add to ClientEvent queue
+ ClientEvent event;
+ event.type = CE_PLAYER_DAMAGE;
+ event.player_damage.amount = damage;
+ m_client_event_queue.push_back(event);
+ }
+ }
+ }
+ }
+
+ /*
+ Print some info
+ */
{
float &counter = m_avg_rtt_timer;
counter += dtime;
dstream<<DTIME<<"Client: avg_rtt="<<peer->avg_rtt<<std::endl;
}
}
+
+ /*
+ Send player position to server
+ */
{
float &counter = m_playerpos_send_timer;
counter += dtime;
}
if(r.ack_block_to_server)
{
+ /*dstream<<"Client: ACK block ("<<r.p.X<<","<<r.p.Y
+ <<","<<r.p.Z<<")"<<std::endl;*/
/*
Acknowledge block
*/
void Client::Receive()
{
DSTACK(__FUNCTION_NAME);
- u32 data_maxsize = 10000;
+ u32 data_maxsize = 200000;
Buffer<u8> data(data_maxsize);
u16 sender_peer_id;
u32 datasize;
}
}
}
- else
- {
- dout_client<<DTIME<<"WARNING: Client: Ignoring unknown command "
- <<command<<std::endl;
- }
-#if 0
- // Default to queueing it (for slow commands)
- else
+ else if(command == TOCLIENT_HP)
{
- JMutexAutoLock lock(m_incoming_queue_mutex);
-
- IncomingPacket packet(data, datasize);
- m_incoming_queue.push_back(packet);
- }
-#endif
-}
-
-#if 0
-/*
- Returns true if there was something in queue
-*/
-bool Client::AsyncProcessPacket()
-{
- DSTACK(__FUNCTION_NAME);
-
- try //for catching con::PeerNotFoundException
- {
-
- con::Peer *peer;
- {
- //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
- // All data is coming from the server
- peer = m_con.GetPeer(PEER_ID_SERVER);
- }
-
- u8 ser_version = m_server_ser_ver;
-
- IncomingPacket packet = getPacket();
- u8 *data = packet.m_data;
- u32 datasize = packet.m_datalen;
-
- // An empty packet means queue is empty
- if(data == NULL){
- return false;
+ std::string datastring((char*)&data[2], datasize-2);
+ std::istringstream is(datastring, std::ios_base::binary);
+ Player *player = m_env.getLocalPlayer();
+ assert(player != NULL);
+ u8 hp = readU8(is);
+ player->hp = hp;
}
-
- if(datasize < 2)
- return true;
-
- ToClientCommand command = (ToClientCommand)readU16(&data[0]);
-
- if(command == TOCLIENT_BLOCKDATA)
+ else if(command == TOCLIENT_MOVE_PLAYER)
{
- // Ignore too small packet
- if(datasize < 8)
- return true;
-
- v3s16 p;
- p.X = readS16(&data[2]);
- p.Y = readS16(&data[4]);
- p.Z = readS16(&data[6]);
-
- /*dout_client<<DTIME<<"Client: Thread: BLOCKDATA for ("
- <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
- /*dstream<<DTIME<<"Client: Thread: BLOCKDATA for ("
- <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
-
- std::string datastring((char*)&data[8], datasize-8);
- std::istringstream istr(datastring, std::ios_base::binary);
-
- MapSector *sector;
- MapBlock *block;
-
- { //envlock
- //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
-
- v2s16 p2d(p.X, p.Z);
- sector = m_env.getMap().emergeSector(p2d);
-
- v2s16 sp = sector->getPos();
- if(sp != p2d)
- {
- dstream<<"ERROR: Got sector with getPos()="
- <<"("<<sp.X<<","<<sp.Y<<"), tried to get"
- <<"("<<p2d.X<<","<<p2d.Y<<")"<<std::endl;
- }
-
- assert(sp == p2d);
- //assert(sector->getPos() == p2d);
-
- //TimeTaker timer("MapBlock deSerialize");
- // 0ms
-
- try{
- block = sector->getBlockNoCreate(p.Y);
- /*
- Update an existing block
- */
- //dstream<<"Updating"<<std::endl;
- 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
-
- /*
- Acknowledge block.
- */
- /*
- [0] u16 command
- [2] u8 count
- [3] v3s16 pos_0
- [3+6] v3s16 pos_1
- ...
- */
- u32 replysize = 2+1+6;
- SharedBuffer<u8> reply(replysize);
- writeU16(&reply[0], TOSERVER_GOTBLOCKS);
- reply[2] = 1;
- writeV3S16(&reply[3], p);
- // Send as reliable
- m_con.Send(PEER_ID_SERVER, 1, reply, true);
+ std::string datastring((char*)&data[2], datasize-2);
+ std::istringstream is(datastring, std::ios_base::binary);
+ Player *player = m_env.getLocalPlayer();
+ assert(player != NULL);
+ v3f pos = readV3F1000(is);
+ f32 pitch = readF1000(is);
+ f32 yaw = readF1000(is);
+ player->setPosition(pos);
+ /*player->setPitch(pitch);
+ player->setYaw(yaw);*/
+
+ dstream<<"Client got TOCLIENT_MOVE_PLAYER"
+ <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
+ <<" pitch="<<pitch
+ <<" yaw="<<yaw
+ <<std::endl;
/*
- Update Mesh of this block and blocks at x-, y- and z-.
- Environment should not be locked as it interlocks with the
- main thread, from which is will want to retrieve textures.
+ Add to ClientEvent queue.
+ This has to be sent to the main program because otherwise
+ it would just force the pitch and yaw values to whatever
+ the camera points to.
*/
-
- //m_env.getClientMap().updateMeshes(block->getPos(), getDayNightRatio());
-
- MeshMakeData data;
- {
- //TimeTaker timer("data fill");
- // 0ms
- data.fill(getDayNightRatio(), block);
- }
- {
- TimeTaker timer("make mesh");
- scene::SMesh *mesh_new = NULL;
- mesh_new = makeMapBlockMesh(&data);
- block->replaceMesh(mesh_new);
- }
+ ClientEvent event;
+ event.type = CE_PLAYER_FORCE_MOVE;
+ event.player_force_move.pitch = pitch;
+ event.player_force_move.yaw = yaw;
+ m_client_event_queue.push_back(event);
+
+ // Ignore damage for a few seconds, so that the player doesn't
+ // get damage from falling on ground
+ m_ignore_damage_timer = 3.0;
}
else
{
dout_client<<DTIME<<"WARNING: Client: Ignoring unknown command "
<<command<<std::endl;
}
-
- return true;
-
- } //try
- catch(con::PeerNotFoundException &e)
- {
- /*dout_client<<DTIME<<"Client::AsyncProcessData(): Cancelling: The server"
- " connection doesn't exist (a timeout or not yet connected?)"<<std::endl;*/
- return false;
- }
-}
-
-bool Client::AsyncProcessData()
-{
- for(;;)
- {
- bool r = AsyncProcessPacket();
- if(r == false)
- break;
- }
- return false;
}
-#endif
void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
{
m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
}
-#if 0
-IncomingPacket Client::getPacket()
-{
- JMutexAutoLock lock(m_incoming_queue_mutex);
-
- core::list<IncomingPacket>::Iterator i;
- // Refer to first one
- i = m_incoming_queue.begin();
-
- // If queue is empty, return empty packet
- if(i == m_incoming_queue.end()){
- IncomingPacket packet;
- return packet;
- }
-
- // Pop out first packet and return it
- IncomingPacket packet = *i;
- m_incoming_queue.erase(i);
- return packet;
-}
-#endif
-
void Client::groundAction(u8 action, v3s16 nodepos_undersurface,
v3s16 nodepos_oversurface, u16 item)
{
Send(0, data, true);
}
+void Client::sendDamage(u8 damage)
+{
+ DSTACK(__FUNCTION_NAME);
+ std::ostringstream os(std::ios_base::binary);
+
+ writeU16(os, TOSERVER_DAMAGE);
+ writeU8(os, damage);
+
+ // Make data buffer
+ std::string s = os.str();
+ SharedBuffer<u8> data((u8*)s.c_str(), s.size());
+ // Send as reliable
+ Send(0, data, true);
+}
+
void Client::sendPlayerPos()
{
//JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
return m_env.getDayNightRatio();
}
+u16 Client::getHP()
+{
+ Player *player = m_env.getLocalPlayer();
+ assert(player != NULL);
+ return player->hp;
+}
+
void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server)
{
/*dstream<<"Client::addUpdateMeshTask(): "
catch(InvalidPositionException &e){}
}
+ClientEvent Client::getClientEvent()
+{
+ if(m_client_event_queue.size() == 0)
+ {
+ ClientEvent event;
+ event.type = CE_NONE;
+ return event;
+ }
+ return m_client_event_queue.pop_front();
+}
+
+
MutexedQueue<MeshUpdateResult> m_queue_out;
};
-#if 0
-struct IncomingPacket
+enum ClientEventType
{
- IncomingPacket()
- {
- m_data = NULL;
- m_datalen = 0;
- m_refcount = NULL;
- }
- IncomingPacket(const IncomingPacket &a)
- {
- m_data = a.m_data;
- m_datalen = a.m_datalen;
- m_refcount = a.m_refcount;
- if(m_refcount != NULL)
- (*m_refcount)++;
- }
- IncomingPacket(u8 *data, u32 datalen)
- {
- m_data = new u8[datalen];
- memcpy(m_data, data, datalen);
- m_datalen = datalen;
- m_refcount = new s32(1);
- }
- ~IncomingPacket()
- {
- if(m_refcount != NULL){
- assert(*m_refcount > 0);
- (*m_refcount)--;
- if(*m_refcount == 0){
- if(m_data != NULL)
- delete[] m_data;
- delete m_refcount;
- }
- }
- }
- /*IncomingPacket & operator=(IncomingPacket a)
- {
- m_data = a.m_data;
- m_datalen = a.m_datalen;
- m_refcount = a.m_refcount;
- (*m_refcount)++;
- return *this;
- }*/
- u8 *m_data;
- u32 m_datalen;
- s32 *m_refcount;
+ CE_NONE,
+ CE_PLAYER_DAMAGE,
+ CE_PLAYER_FORCE_MOVE
+};
+
+struct ClientEvent
+{
+ ClientEventType type;
+ union{
+ struct{
+ } none;
+ struct{
+ u8 amount;
+ } player_damage;
+ struct{
+ f32 pitch;
+ f32 yaw;
+ } player_force_move;
+ };
};
-#endif
class Client : public con::PeerHandler, public InventoryManager
{
void sendSignNodeText(v3s16 p, std::string text);
void sendInventoryAction(InventoryAction *a);
void sendChatMessage(const std::wstring &message);
+ void sendDamage(u8 damage);
// locks envlock
void removeNode(v3s16 p);
u32 getDayNightRatio();
+ u16 getHP();
+
//void updateSomeExpiredMeshes();
void setTempMod(v3s16 p, NodeMod mod)
u64 getMapSeed(){ return m_map_seed; }
- /*
- These are not thread-safe
- */
void addUpdateMeshTask(v3s16 blockpos, bool ack_to_server=false);
// Including blocks at appropriate edges
void addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server=false);
+ // Get event from queue. CE_NONE is returned if queue is empty.
+ ClientEvent getClientEvent();
+
private:
// Virtual methods from con::PeerHandler
float m_connection_reinit_timer;
float m_avg_rtt_timer;
float m_playerpos_send_timer;
+ float m_ignore_damage_timer; // Used after server moves player
MeshUpdateThread m_mesh_update_thread;
u64 m_map_seed;
InventoryContext m_inventory_context;
+
+ Queue<ClientEvent> m_client_event_queue;
};
#endif // !SERVER
v3s16 RatCAO::getLightPosition()
{
- return floatToInt(m_position, BS);
+ return floatToInt(m_position+v3f(0,BS*0.5,0), BS);
}
void RatCAO::updateNodePos()
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(porting::getDataPath("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;
+
+ 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(objectpos_2d.Y - playerpos_2d.Y) < 2.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();
+}
+
#include "common_irrlicht.h"
#include "activeobject.h"
+#include "utility.h"
/*
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
[0] u16 TOSERVER_INIT
[2] u8 deployed version
- [3] v3s16 player's position + v3f(0,BS/2,0) floatToInt'd
- [4] u64 map seed (new as of 2011-02-27)
+ [3] v3s16 player's position + v3f(0,BS/2,0) floatToInt'd
+ ([4] u64 map seed (new as of 2011-02-27))
+
+ NOTE: The position in here is deprecated; position is
+ explicitly sent afterwards
*/
TOCLIENT_BLOCKDATA = 0x20, //TODO: Multiple blocks
TOCLIENT_ADDNODE = 0x21,
TOCLIENT_REMOVENODE = 0x22,
- TOCLIENT_PLAYERPOS = 0x23,
+ TOCLIENT_PLAYERPOS = 0x23, // Obsolete
/*
[0] u16 command
// Followed by an arbitary number of these:
[N] char[20] name
*/
- TOCLIENT_OPT_BLOCK_NOT_FOUND = 0x25, // Not used
+ TOCLIENT_OPT_BLOCK_NOT_FOUND = 0x25, // Obsolete
- TOCLIENT_SECTORMETA = 0x26, // Not used
+ TOCLIENT_SECTORMETA = 0x26, // Obsolete
/*
[0] u16 command
[2] u8 sector count
}
*/
+ TOCLIENT_HP = 0x33,
+ /*
+ u16 command
+ u8 hp
+ */
+
+ TOCLIENT_MOVE_PLAYER = 0x34,
+ /*
+ u16 command
+ v3f1000 player position
+ f1000 player pitch
+ f1000 player yaw
+ */
};
enum ToServerCommand
[0] u16 TOSERVER_INIT2
*/
- TOSERVER_GETBLOCK=0x20, // Not used
- TOSERVER_ADDNODE = 0x21, // Not used
- TOSERVER_REMOVENODE = 0x22, // deprecated
+ TOSERVER_GETBLOCK=0x20, // Obsolete
+ TOSERVER_ADDNODE = 0x21, // Obsolete
+ TOSERVER_REMOVENODE = 0x22, // Obsolete
TOSERVER_PLAYERPOS = 0x23,
/*
...
*/
- TOSERVER_ADDNODE_FROM_INVENTORY = 0x26, // deprecated
+ TOSERVER_ADDNODE_FROM_INVENTORY = 0x26, // Obsolete
/*
[0] u16 command
[2] v3s16 pos
3: digging completed
*/
- TOSERVER_RELEASE = 0x29, // Not used
+ TOSERVER_RELEASE = 0x29, // Obsolete
- TOSERVER_SIGNTEXT = 0x30,
+ TOSERVER_SIGNTEXT = 0x30, // Old signs
/*
u16 command
v3s16 blockpos
[3] u16 id
[5] u16 item
*/
-
+
+ TOSERVER_DAMAGE = 0x35,
+ /*
+ u16 command
+ u8 amount
+ */
};
inline SharedBuffer<u8> makePacket_TOCLIENT_TIME_OF_DAY(u16 time)
/*
Go through every node around the object
+ TODO: Calculate the range of nodes that need to be checked
*/
for(s16 y = oldpos_i.Y - 1; y <= oldpos_i.Y + 2; y++)
for(s16 z = oldpos_i.Z - 1; z <= oldpos_i.Z + 1; z++)
f32 dtime, v3f &pos_f, v3f &speed_f);
//{return collisionMoveResult();}
+enum CollisionType
+{
+ COLLISION_FALL
+};
+
+struct CollisionInfo
+{
+ CollisionType t;
+ f32 speed;
+};
#endif
g_settings.setDefault("enable_experimental", "false");
g_settings.setDefault("creative_mode", "false");
+ g_settings.setDefault("enable_damage", "false"); //TODO: Set to true
g_settings.setDefault("objectdata_interval", "0.2");
g_settings.setDefault("active_object_range", "2");
#include "environment.h"
#include "filesys.h"
#include "porting.h"
+#include "collision.h"
Environment::Environment()
{
}
}
+#if 0
+void spawnRandomObjects(MapBlock *block)
+{
+ for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
+ for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
+ {
+ bool last_node_walkable = false;
+ for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
+ {
+ v3s16 p(x0,y0,z0);
+ MapNode n = block->getNodeNoEx(p);
+ if(n.d == CONTENT_IGNORE)
+ continue;
+ if(content_features(n.d).liquid_type != LIQUID_NONE)
+ continue;
+ if(content_features(n.d).walkable)
+ {
+ last_node_walkable = true;
+ continue;
+ }
+ if(last_node_walkable)
+ {
+ // If block contains light information
+ if(content_features(n.d).param_type == CPT_LIGHT)
+ {
+ if(n.getLight(LIGHTBANK_DAY) <= 5)
+ {
+ if(myrand() % 1000 == 0)
+ {
+ v3f pos_f = intToFloat(p+block->getPosRelative(), BS);
+ pos_f.Y -= BS*0.4;
+ ServerActiveObject *obj = new Oerkki1SAO(NULL,0,pos_f);
+ std::string data = obj->getStaticData();
+ StaticObject s_obj(obj->getType(),
+ obj->getBasePosition(), data);
+ // Add one
+ block->m_static_objects.insert(0, s_obj);
+ delete obj;
+ block->setChangedFlag();
+ }
+ }
+ }
+ }
+ last_node_walkable = false;
+ }
+ }
+}
+#endif
+
void ServerEnvironment::step(float dtime)
{
DSTACK(__FUNCTION_NAME);
}
}
}
-
+
/*
Step active objects
*/
-
- bool send_recommended = false;
- m_send_recommended_timer += dtime;
- if(m_send_recommended_timer > 0.15)
{
- m_send_recommended_timer = 0;
- send_recommended = true;
- }
+ //TimeTaker timer("Step active objects");
- for(core::map<u16, ServerActiveObject*>::Iterator
- i = m_active_objects.getIterator();
- i.atEnd()==false; i++)
- {
- ServerActiveObject* obj = i.getNode()->getValue();
- // Step object, putting messages directly to the queue
- obj->step(dtime, m_active_object_messages, send_recommended);
+ bool send_recommended = false;
+ m_send_recommended_timer += dtime;
+ if(m_send_recommended_timer > 0.15)
+ {
+ m_send_recommended_timer = 0;
+ send_recommended = true;
+ }
+
+ for(core::map<u16, ServerActiveObject*>::Iterator
+ i = m_active_objects.getIterator();
+ i.atEnd()==false; i++)
+ {
+ ServerActiveObject* obj = i.getNode()->getValue();
+ // Step object, putting messages directly to the queue
+ obj->step(dtime, m_active_object_messages, send_recommended);
+ }
}
if(m_object_management_interval.step(dtime, 0.5))
const s16 to_active_max_blocks = 3;
- const f32 to_static_max_f = (to_active_max_blocks+1)*MAP_BLOCKSIZE*BS;
+ const f32 to_static_max_f = (to_active_max_blocks+2)*MAP_BLOCKSIZE*BS;
/*
Convert stored objects from blocks near the players to active.
//TestSAO *obj = new TestSAO(this, 0, pos);
//ServerActiveObject *obj = new ItemSAO(this, 0, pos, "CraftItem Stick 1");
- ServerActiveObject *obj = new RatSAO(this, 0, pos);
+ //ServerActiveObject *obj = new RatSAO(this, 0, pos);
+ ServerActiveObject *obj = new Oerkki1SAO(this, 0, pos);
addActiveObject(obj);
}
#endif
//TimeTaker timer("Client m_map->timerUpdate()", g_device);
m_map->timerUpdate(dtime);
}
-
+
+ // Get local player
+ LocalPlayer *lplayer = getLocalPlayer();
+ assert(lplayer);
+ // collision info queue
+ core::list<CollisionInfo> player_collisions;
+
/*
Get the speed the player is going
*/
f32 player_speed = 0.001; // just some small value
- LocalPlayer *lplayer = getLocalPlayer();
- if(lplayer)
- player_speed = lplayer->getSpeed().getLength();
+ player_speed = lplayer->getSpeed().getLength();
/*
Maximum position increment
*/
{
- Player *player = getLocalPlayer();
-
- v3f playerpos = player->getPosition();
+ v3f lplayerpos = lplayer->getPosition();
// Apply physics
if(free_move == false)
{
// Gravity
- v3f speed = player->getSpeed();
- if(player->swimming_up == false)
+ v3f speed = lplayer->getSpeed();
+ if(lplayer->swimming_up == false)
speed.Y -= 9.81 * BS * dtime_part * 2;
// Water resistance
- if(player->in_water_stable || player->in_water)
+ if(lplayer->in_water_stable || lplayer->in_water)
{
f32 max_down = 2.0*BS;
if(speed.Y < -max_down) speed.Y = -max_down;
}
}
- player->setSpeed(speed);
+ lplayer->setSpeed(speed);
}
/*
- Move the player.
+ Move the lplayer.
This also does collision detection.
*/
- player->move(dtime_part, *m_map, position_max_increment);
+ lplayer->move(dtime_part, *m_map, position_max_increment,
+ &player_collisions);
}
}
while(dtime_downcount > 0.001);
//std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
+
+ for(core::list<CollisionInfo>::Iterator
+ i = player_collisions.begin();
+ i != player_collisions.end(); i++)
+ {
+ CollisionInfo &info = *i;
+ if(info.t == COLLISION_FALL)
+ {
+ //f32 tolerance = BS*10; // 2 without damage
+ f32 tolerance = BS*12; // 3 without damage
+ f32 factor = 1;
+ if(info.speed > tolerance)
+ {
+ f32 damage_f = (info.speed - tolerance)/BS*factor;
+ u16 damage = (u16)(damage_f+0.5);
+ if(lplayer->hp > damage)
+ lplayer->hp -= damage;
+ else
+ lplayer->hp = 0;
+
+ ClientEnvEvent event;
+ event.type = CEE_PLAYER_DAMAGE;
+ event.player_damage.amount = damage;
+ m_client_event_queue.push_back(event);
+ }
+ }
+ }
/*
Stuff that can be done in an arbitarily large dtime
obj->processMessage(data);
}
+/*
+ Callbacks for activeobjects
+*/
+
+void ClientEnvironment::damageLocalPlayer(u8 damage)
+{
+ LocalPlayer *lplayer = getLocalPlayer();
+ assert(lplayer);
+
+ if(lplayer->hp > damage)
+ lplayer->hp -= damage;
+ else
+ lplayer->hp = 0;
+
+ ClientEnvEvent event;
+ event.type = CEE_PLAYER_DAMAGE;
+ event.player_damage.amount = damage;
+ m_client_event_queue.push_back(event);
+}
+
+/*
+ Client likes to call these
+*/
+
void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
core::array<DistanceSortedActiveObject> &dest)
{
}
}
+ClientEnvEvent ClientEnvironment::getClientEvent()
+{
+ if(m_client_event_queue.size() == 0)
+ {
+ ClientEnvEvent event;
+ event.type = CEE_NONE;
+ return event;
+ }
+ return m_client_event_queue.pop_front();
+}
#endif // #ifndef SERVER
Client uses an environment mutex.
*/
+enum ClientEnvEventType
+{
+ CEE_NONE,
+ CEE_PLAYER_DAMAGE
+};
+
+struct ClientEnvEvent
+{
+ ClientEnvEventType type;
+ union {
+ struct{
+ } none;
+ struct{
+ u8 amount;
+ } player_damage;
+ };
+};
+
class ClientEnvironment : public Environment
{
public:
void removeActiveObject(u16 id);
void processActiveObjectMessage(u16 id, const std::string &data);
+
+ /*
+ Callbacks for activeobjects
+ */
+
+ void damageLocalPlayer(u8 damage);
+
+ /*
+ Client likes to call these
+ */
// Get all nearby objects
void getActiveObjects(v3f origin, f32 max_d,
core::array<DistanceSortedActiveObject> &dest);
+ // Get event from queue. CEE_NONE is returned if queue is empty.
+ ClientEnvEvent getClientEvent();
+
private:
ClientMap *m_map;
scene::ISceneManager *m_smgr;
core::map<u16, ClientActiveObject*> m_active_objects;
+ Queue<ClientEnvEvent> m_client_event_queue;
};
#endif
basename = "tool_stoneaxe.png";
else if(m_toolname == "SteelAxe")
basename = "tool_steelaxe.png";
+ else if(m_toolname == "WSword")
+ basename = "tool_woodsword.png";
+ else if(m_toolname == "STSword")
+ basename = "tool_stonesword.png";
+ else if(m_toolname == "SteelSword")
+ basename = "tool_steelsword.png";
else
basename = "cloud.png";
SUGG: Calculate lighting per vertex to get a lighting effect like in\r
bartwe's game\r
\r
+SUGG: Background music based on cellular automata?\r
+ http://www.earslap.com/projectslab/otomata\r
+\r
+\r
Gaming ideas:\r
-------------\r
\r
- You can drop on top of it, and have some time to attack there\r
before he shakes you off\r
\r
+- Maybe the difficulty could come from monsters getting tougher in\r
+ far-away places, and the player starting to need something from\r
+ there when time goes by.\r
+ - The player would have some of that stuff at the beginning, and\r
+ would need new supplies of it when it runs out\r
+\r
Documentation:\r
--------------\r
\r
\r
void draw_hotbar(video::IVideoDriver *driver, gui::IGUIFont *font,\r
v2s32 centerlowerpos, s32 imgsize, s32 itemcount,\r
- Inventory *inventory)\r
+ Inventory *inventory, s32 halfheartcount)\r
{\r
InventoryList *mainlist = inventory->getList("main");\r
if(mainlist == NULL)\r
drawInventoryItem(driver, font, item, rect, NULL);\r
}\r
}\r
+ \r
+ /*\r
+ Draw hearts\r
+ */\r
+ {\r
+ video::ITexture *heart_texture =\r
+ driver->getTexture(porting::getDataPath("heart.png").c_str());\r
+ v2s32 p = pos + v2s32(0, -20);\r
+ for(s32 i=0; i<halfheartcount/2; i++)\r
+ {\r
+ const video::SColor color(255,255,255,255);\r
+ const video::SColor colors[] = {color,color,color,color};\r
+ core::rect<s32> rect(0,0,16,16);\r
+ rect += p;\r
+ driver->draw2DImage(heart_texture, rect,\r
+ core::rect<s32>(core::position2d<s32>(0,0),\r
+ core::dimension2di(heart_texture->getOriginalSize())),\r
+ NULL, colors, true);\r
+ p += v2s32(20,0);\r
+ }\r
+ if(halfheartcount % 2 == 1)\r
+ {\r
+ const video::SColor color(255,255,255,255);\r
+ const video::SColor colors[] = {color,color,color,color};\r
+ core::rect<s32> rect(0,0,16/2,16);\r
+ rect += p;\r
+ core::dimension2di srcd(heart_texture->getOriginalSize());\r
+ srcd.Width /= 2;\r
+ driver->draw2DImage(heart_texture, rect,\r
+ core::rect<s32>(core::position2d<s32>(0,0), srcd),\r
+ NULL, colors, true);\r
+ p += v2s32(20,0);\r
+ }\r
+ }\r
}\r
\r
#if 0\r
}\r
}\r
\r
+void getPointedNode(v3f player_position,\r
+ v3f camera_direction, v3f camera_position,\r
+ bool &nodefound, core::line3d<f32> shootline,\r
+ v3s16 &nodepos, v3s16 &neighbourpos,\r
+ core::aabbox3d<f32> &nodehilightbox,\r
+ f32 d)\r
+{\r
+ assert(g_client);\r
+\r
+ f32 mindistance = BS * 1001;\r
+ \r
+ v3s16 pos_i = floatToInt(player_position, BS);\r
+\r
+ /*std::cout<<"pos_i=("<<pos_i.X<<","<<pos_i.Y<<","<<pos_i.Z<<")"\r
+ <<std::endl;*/\r
+\r
+ s16 a = d;\r
+ s16 ystart = pos_i.Y + 0 - (camera_direction.Y<0 ? a : 1);\r
+ s16 zstart = pos_i.Z - (camera_direction.Z<0 ? a : 1);\r
+ s16 xstart = pos_i.X - (camera_direction.X<0 ? a : 1);\r
+ s16 yend = pos_i.Y + 1 + (camera_direction.Y>0 ? a : 1);\r
+ s16 zend = pos_i.Z + (camera_direction.Z>0 ? a : 1);\r
+ s16 xend = pos_i.X + (camera_direction.X>0 ? a : 1);\r
+ \r
+ for(s16 y = ystart; y <= yend; y++)\r
+ for(s16 z = zstart; z <= zend; z++)\r
+ for(s16 x = xstart; x <= xend; x++)\r
+ {\r
+ MapNode n;\r
+ try\r
+ {\r
+ n = g_client->getNode(v3s16(x,y,z));\r
+ if(content_pointable(n.d) == false)\r
+ continue;\r
+ }\r
+ catch(InvalidPositionException &e)\r
+ {\r
+ continue;\r
+ }\r
+\r
+ v3s16 np(x,y,z);\r
+ v3f npf = intToFloat(np, BS);\r
+ \r
+ f32 d = 0.01;\r
+ \r
+ v3s16 dirs[6] = {\r
+ v3s16(0,0,1), // back\r
+ v3s16(0,1,0), // top\r
+ v3s16(1,0,0), // right\r
+ v3s16(0,0,-1), // front\r
+ v3s16(0,-1,0), // bottom\r
+ v3s16(-1,0,0), // left\r
+ };\r
+ \r
+ /*\r
+ Meta-objects\r
+ */\r
+ if(n.d == CONTENT_TORCH)\r
+ {\r
+ v3s16 dir = unpackDir(n.dir);\r
+ v3f dir_f = v3f(dir.X, dir.Y, dir.Z);\r
+ dir_f *= BS/2 - BS/6 - BS/20;\r
+ v3f cpf = npf + dir_f;\r
+ f32 distance = (cpf - camera_position).getLength();\r
+\r
+ core::aabbox3d<f32> box;\r
+ \r
+ // bottom\r
+ if(dir == v3s16(0,-1,0))\r
+ {\r
+ box = core::aabbox3d<f32>(\r
+ npf - v3f(BS/6, BS/2, BS/6),\r
+ npf + v3f(BS/6, -BS/2+BS/3*2, BS/6)\r
+ );\r
+ }\r
+ // top\r
+ else if(dir == v3s16(0,1,0))\r
+ {\r
+ box = core::aabbox3d<f32>(\r
+ npf - v3f(BS/6, -BS/2+BS/3*2, BS/6),\r
+ npf + v3f(BS/6, BS/2, BS/6)\r
+ );\r
+ }\r
+ // side\r
+ else\r
+ {\r
+ box = core::aabbox3d<f32>(\r
+ cpf - v3f(BS/6, BS/3, BS/6),\r
+ cpf + v3f(BS/6, BS/3, BS/6)\r
+ );\r
+ }\r
+\r
+ if(distance < mindistance)\r
+ {\r
+ if(box.intersectsWithLine(shootline))\r
+ {\r
+ nodefound = true;\r
+ nodepos = np;\r
+ neighbourpos = np;\r
+ mindistance = distance;\r
+ nodehilightbox = box;\r
+ }\r
+ }\r
+ }\r
+ else if(n.d == CONTENT_SIGN_WALL)\r
+ {\r
+ v3s16 dir = unpackDir(n.dir);\r
+ v3f dir_f = v3f(dir.X, dir.Y, dir.Z);\r
+ dir_f *= BS/2 - BS/6 - BS/20;\r
+ v3f cpf = npf + dir_f;\r
+ f32 distance = (cpf - camera_position).getLength();\r
+\r
+ v3f vertices[4] =\r
+ {\r
+ v3f(BS*0.42,-BS*0.35,-BS*0.4),\r
+ v3f(BS*0.49, BS*0.35, BS*0.4),\r
+ };\r
+\r
+ for(s32 i=0; i<2; i++)\r
+ {\r
+ if(dir == v3s16(1,0,0))\r
+ vertices[i].rotateXZBy(0);\r
+ if(dir == v3s16(-1,0,0))\r
+ vertices[i].rotateXZBy(180);\r
+ if(dir == v3s16(0,0,1))\r
+ vertices[i].rotateXZBy(90);\r
+ if(dir == v3s16(0,0,-1))\r
+ vertices[i].rotateXZBy(-90);\r
+ if(dir == v3s16(0,-1,0))\r
+ vertices[i].rotateXYBy(-90);\r
+ if(dir == v3s16(0,1,0))\r
+ vertices[i].rotateXYBy(90);\r
+\r
+ vertices[i] += npf;\r
+ }\r
+\r
+ core::aabbox3d<f32> box;\r
+\r
+ box = core::aabbox3d<f32>(vertices[0]);\r
+ box.addInternalPoint(vertices[1]);\r
+\r
+ if(distance < mindistance)\r
+ {\r
+ if(box.intersectsWithLine(shootline))\r
+ {\r
+ nodefound = true;\r
+ nodepos = np;\r
+ neighbourpos = np;\r
+ mindistance = distance;\r
+ nodehilightbox = box;\r
+ }\r
+ }\r
+ }\r
+ /*\r
+ Regular blocks\r
+ */\r
+ else\r
+ {\r
+ for(u16 i=0; i<6; i++)\r
+ {\r
+ v3f dir_f = v3f(dirs[i].X,\r
+ dirs[i].Y, dirs[i].Z);\r
+ v3f centerpoint = npf + dir_f * BS/2;\r
+ f32 distance =\r
+ (centerpoint - camera_position).getLength();\r
+ \r
+ if(distance < mindistance)\r
+ {\r
+ core::CMatrix4<f32> m;\r
+ m.buildRotateFromTo(v3f(0,0,1), dir_f);\r
+\r
+ // This is the back face\r
+ v3f corners[2] = {\r
+ v3f(BS/2, BS/2, BS/2),\r
+ v3f(-BS/2, -BS/2, BS/2+d)\r
+ };\r
+ \r
+ for(u16 j=0; j<2; j++)\r
+ {\r
+ m.rotateVect(corners[j]);\r
+ corners[j] += npf;\r
+ }\r
+\r
+ core::aabbox3d<f32> facebox(corners[0]);\r
+ facebox.addInternalPoint(corners[1]);\r
+\r
+ if(facebox.intersectsWithLine(shootline))\r
+ {\r
+ nodefound = true;\r
+ nodepos = np;\r
+ neighbourpos = np + dirs[i];\r
+ mindistance = distance;\r
+\r
+ //nodehilightbox = facebox;\r
+\r
+ const float d = 0.502;\r
+ core::aabbox3d<f32> nodebox\r
+ (-BS*d, -BS*d, -BS*d, BS*d, BS*d, BS*d);\r
+ v3f nodepos_f = intToFloat(nodepos, BS);\r
+ nodebox.MinEdge += nodepos_f;\r
+ nodebox.MaxEdge += nodepos_f;\r
+ nodehilightbox = nodebox;\r
+ }\r
+ } // if distance < mindistance\r
+ } // for dirs\r
+ } // regular block\r
+ } // for coords\r
+}\r
+\r
int main(int argc, char *argv[])\r
{\r
/*\r
\r
//video::SColor skycolor = video::SColor(255,90,140,200);\r
//video::SColor skycolor = video::SColor(255,166,202,244);\r
- video::SColor skycolor = video::SColor(255,120,185,244);\r
+ //video::SColor skycolor = video::SColor(255,120,185,244);\r
+ video::SColor skycolor = video::SColor(255,140,186,250);\r
\r
camera->setFOV(FOV_ANGLE);\r
\r
// Just so big a value that everything rendered is visible\r
camera->setFarValue(100000*BS);\r
\r
- /*\r
- Lighting test code. Doesn't quite work this way.\r
- The CPU-computed lighting is good.\r
- */\r
-\r
- /*\r
- smgr->addLightSceneNode(NULL,\r
- v3f(0, BS*1000000, 0),\r
- video::SColorf(0.3,0.3,0.3),\r
- BS*10000000);\r
-\r
- smgr->setAmbientLight(video::SColorf(0.0, 0.0, 0.0));\r
-\r
- scene::ILightSceneNode *light = smgr->addLightSceneNode(camera,\r
- v3f(0, 0, 0), video::SColorf(0.5,0.5,0.5), BS*4);\r
- */\r
-\r
f32 camera_yaw = 0; // "right/left"\r
f32 camera_pitch = 0; // "up/down"\r
\r
\r
core::list<float> frametime_log;\r
\r
+ float damage_flash_timer = 0;\r
+\r
/*\r
Main loop\r
*/\r
);\r
client.setPlayerControl(control);\r
}\r
+ \r
+ /*\r
+ Run server\r
+ */\r
+\r
+ if(server != NULL)\r
+ {\r
+ //TimeTaker timer("server->step(dtime)");\r
+ server->step(dtime);\r
+ }\r
\r
/*\r
Process environment\r
//client.step(dtime_avg1);\r
}\r
\r
- if(server != NULL)\r
+ // Read client events\r
+ for(;;)\r
{\r
- //TimeTaker timer("server->step(dtime)");\r
- server->step(dtime);\r
+ ClientEvent event = client.getClientEvent();\r
+ if(event.type == CE_NONE)\r
+ {\r
+ break;\r
+ }\r
+ else if(event.type == CE_PLAYER_DAMAGE)\r
+ {\r
+ //u16 damage = event.player_damage.amount;\r
+ //dstream<<"Player damage: "<<damage<<std::endl;\r
+ damage_flash_timer = 0.05;\r
+ }\r
+ else if(event.type == CE_PLAYER_FORCE_MOVE)\r
+ {\r
+ camera_yaw = event.player_force_move.yaw;\r
+ camera_pitch = event.player_force_move.pitch;\r
+ }\r
}\r
-\r
+ \r
+ // Get player position\r
v3f player_position = client.getPlayerPosition();\r
\r
//TimeTaker //timer2("//timer2");\r
else if(g_input->getRightClicked())\r
{\r
std::cout<<DTIME<<"Right-clicked object"<<std::endl;\r
-#if 0\r
- /*\r
- Check if we want to modify the object ourselves\r
- */\r
- if(selected_object->getTypeId() == MAPBLOCKOBJECT_TYPE_SIGN)\r
- {\r
- }\r
- /*\r
- Otherwise pass the event to the server as-is\r
- */\r
- else\r
- {\r
- client.clickObject(1, selected_object->getBlock()->getPos(),\r
- selected_object->getId(), g_selected_item);\r
- }\r
-#endif\r
}\r
}\r
else // selected_object == NULL\r
v3s16 nodepos;\r
v3s16 neighbourpos;\r
core::aabbox3d<f32> nodehilightbox;\r
- f32 mindistance = BS * 1001;\r
- \r
- v3s16 pos_i = floatToInt(player_position, BS);\r
-\r
- /*std::cout<<"pos_i=("<<pos_i.X<<","<<pos_i.Y<<","<<pos_i.Z<<")"\r
- <<std::endl;*/\r
-\r
- s16 a = d;\r
- s16 ystart = pos_i.Y + 0 - (camera_direction.Y<0 ? a : 1);\r
- s16 zstart = pos_i.Z - (camera_direction.Z<0 ? a : 1);\r
- s16 xstart = pos_i.X - (camera_direction.X<0 ? a : 1);\r
- s16 yend = pos_i.Y + 1 + (camera_direction.Y>0 ? a : 1);\r
- s16 zend = pos_i.Z + (camera_direction.Z>0 ? a : 1);\r
- s16 xend = pos_i.X + (camera_direction.X>0 ? a : 1);\r
- \r
- for(s16 y = ystart; y <= yend; y++)\r
- for(s16 z = zstart; z <= zend; z++)\r
- for(s16 x = xstart; x <= xend; x++)\r
- {\r
- MapNode n;\r
- try\r
- {\r
- n = client.getNode(v3s16(x,y,z));\r
- if(content_pointable(n.d) == false)\r
- continue;\r
- }\r
- catch(InvalidPositionException &e)\r
- {\r
- continue;\r
- }\r
-\r
- v3s16 np(x,y,z);\r
- v3f npf = intToFloat(np, BS);\r
- \r
- f32 d = 0.01;\r
- \r
- v3s16 dirs[6] = {\r
- v3s16(0,0,1), // back\r
- v3s16(0,1,0), // top\r
- v3s16(1,0,0), // right\r
- v3s16(0,0,-1), // front\r
- v3s16(0,-1,0), // bottom\r
- v3s16(-1,0,0), // left\r
- };\r
- \r
- /*\r
- Meta-objects\r
- */\r
- if(n.d == CONTENT_TORCH)\r
- {\r
- v3s16 dir = unpackDir(n.dir);\r
- v3f dir_f = v3f(dir.X, dir.Y, dir.Z);\r
- dir_f *= BS/2 - BS/6 - BS/20;\r
- v3f cpf = npf + dir_f;\r
- f32 distance = (cpf - camera_position).getLength();\r
-\r
- core::aabbox3d<f32> box;\r
- \r
- // bottom\r
- if(dir == v3s16(0,-1,0))\r
- {\r
- box = core::aabbox3d<f32>(\r
- npf - v3f(BS/6, BS/2, BS/6),\r
- npf + v3f(BS/6, -BS/2+BS/3*2, BS/6)\r
- );\r
- }\r
- // top\r
- else if(dir == v3s16(0,1,0))\r
- {\r
- box = core::aabbox3d<f32>(\r
- npf - v3f(BS/6, -BS/2+BS/3*2, BS/6),\r
- npf + v3f(BS/6, BS/2, BS/6)\r
- );\r
- }\r
- // side\r
- else\r
- {\r
- box = core::aabbox3d<f32>(\r
- cpf - v3f(BS/6, BS/3, BS/6),\r
- cpf + v3f(BS/6, BS/3, BS/6)\r
- );\r
- }\r
-\r
- if(distance < mindistance)\r
- {\r
- if(box.intersectsWithLine(shootline))\r
- {\r
- nodefound = true;\r
- nodepos = np;\r
- neighbourpos = np;\r
- mindistance = distance;\r
- nodehilightbox = box;\r
- }\r
- }\r
- }\r
- else if(n.d == CONTENT_SIGN_WALL)\r
- {\r
- v3s16 dir = unpackDir(n.dir);\r
- v3f dir_f = v3f(dir.X, dir.Y, dir.Z);\r
- dir_f *= BS/2 - BS/6 - BS/20;\r
- v3f cpf = npf + dir_f;\r
- f32 distance = (cpf - camera_position).getLength();\r
-\r
- v3f vertices[4] =\r
- {\r
- v3f(BS*0.42,-BS*0.35,-BS*0.4),\r
- v3f(BS*0.49, BS*0.35, BS*0.4),\r
- };\r
-\r
- for(s32 i=0; i<2; i++)\r
- {\r
- if(dir == v3s16(1,0,0))\r
- vertices[i].rotateXZBy(0);\r
- if(dir == v3s16(-1,0,0))\r
- vertices[i].rotateXZBy(180);\r
- if(dir == v3s16(0,0,1))\r
- vertices[i].rotateXZBy(90);\r
- if(dir == v3s16(0,0,-1))\r
- vertices[i].rotateXZBy(-90);\r
- if(dir == v3s16(0,-1,0))\r
- vertices[i].rotateXYBy(-90);\r
- if(dir == v3s16(0,1,0))\r
- vertices[i].rotateXYBy(90);\r
-\r
- vertices[i] += npf;\r
- }\r
-\r
- core::aabbox3d<f32> box;\r
-\r
- box = core::aabbox3d<f32>(vertices[0]);\r
- box.addInternalPoint(vertices[1]);\r
-\r
- if(distance < mindistance)\r
- {\r
- if(box.intersectsWithLine(shootline))\r
- {\r
- nodefound = true;\r
- nodepos = np;\r
- neighbourpos = np;\r
- mindistance = distance;\r
- nodehilightbox = box;\r
- }\r
- }\r
- }\r
- /*\r
- Regular blocks\r
- */\r
- else\r
- {\r
- for(u16 i=0; i<6; i++)\r
- {\r
- v3f dir_f = v3f(dirs[i].X,\r
- dirs[i].Y, dirs[i].Z);\r
- v3f centerpoint = npf + dir_f * BS/2;\r
- f32 distance =\r
- (centerpoint - camera_position).getLength();\r
- \r
- if(distance < mindistance)\r
- {\r
- core::CMatrix4<f32> m;\r
- m.buildRotateFromTo(v3f(0,0,1), dir_f);\r
-\r
- // This is the back face\r
- v3f corners[2] = {\r
- v3f(BS/2, BS/2, BS/2),\r
- v3f(-BS/2, -BS/2, BS/2+d)\r
- };\r
- \r
- for(u16 j=0; j<2; j++)\r
- {\r
- m.rotateVect(corners[j]);\r
- corners[j] += npf;\r
- }\r
-\r
- core::aabbox3d<f32> facebox(corners[0]);\r
- facebox.addInternalPoint(corners[1]);\r
-\r
- if(facebox.intersectsWithLine(shootline))\r
- {\r
- nodefound = true;\r
- nodepos = np;\r
- neighbourpos = np + dirs[i];\r
- mindistance = distance;\r
-\r
- //nodehilightbox = facebox;\r
-\r
- const float d = 0.502;\r
- core::aabbox3d<f32> nodebox\r
- (-BS*d, -BS*d, -BS*d, BS*d, BS*d, BS*d);\r
- v3f nodepos_f = intToFloat(nodepos, BS);\r
- nodebox.MinEdge += nodepos_f;\r
- nodebox.MaxEdge += nodepos_f;\r
- nodehilightbox = nodebox;\r
- }\r
- } // if distance < mindistance\r
- } // for dirs\r
- } // regular block\r
- } // for coords\r
\r
+ getPointedNode(player_position,\r
+ camera_direction, camera_position,\r
+ nodefound, shootline,\r
+ nodepos, neighbourpos,\r
+ nodehilightbox, d);\r
+ \r
static float nodig_delay_counter = 0.0;\r
\r
if(nodefound)\r
*/\r
{\r
draw_hotbar(driver, font, v2s32(displaycenter.X, screensize.Y),\r
- hotbar_imagesize, hotbar_itemcount, &local_inventory);\r
+ hotbar_imagesize, hotbar_itemcount, &local_inventory,\r
+ client.getHP());\r
+ }\r
+\r
+ /*\r
+ Damage flash\r
+ */\r
+ if(damage_flash_timer > 0.0)\r
+ {\r
+ damage_flash_timer -= dtime;\r
+ \r
+ video::SColor color(128,255,0,0);\r
+ driver->draw2DRectangle(color,\r
+ core::rect<s32>(0,0,screensize.X,screensize.Y),\r
+ NULL);\r
}\r
\r
- // End drawing\r
+ /*\r
+ End scene\r
+ */\r
{\r
TimeTaker timer("endScene");\r
driver->endScene();\r
m_dout(dout),
m_sector_cache(NULL)
{
- m_sector_mutex.Init();
- assert(m_sector_mutex.IsInitialized());
+ /*m_sector_mutex.Init();
+ assert(m_sector_mutex.IsInitialized());*/
}
Map::~Map()
MapSector * Map::getSectorNoGenerateNoEx(v2s16 p)
{
- JMutexAutoLock lock(m_sector_mutex);
+ //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
return getSectorNoGenerateNoExNoLock(p);
}
*/
void Map::timerUpdate(float dtime)
{
- JMutexAutoLock lock(m_sector_mutex);
+ //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
core::map<v2s16, MapSector*>::Iterator si;
u32 Map::deleteUnusedSectors(float timeout, bool only_blocks,
core::list<v3s16> *deleted_blocks)
{
- JMutexAutoLock lock(m_sector_mutex);
+ //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
core::list<v2s16> sector_deletion_queue;
core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
block->m_static_objects.insert(0, s_obj);
delete obj;
}
+ if(myrand() % 300 == 0)
+ {
+ v3f pos_f = intToFloat(p+block->getPosRelative(), BS);
+ pos_f.Y -= BS*0.4;
+ ServerActiveObject *obj = new Oerkki1SAO(NULL,0,pos_f);
+ std::string data = obj->getStaticData();
+ StaticObject s_obj(obj->getType(),
+ obj->getBasePosition(), data);
+ // Add one
+ block->m_static_objects.insert(0, s_obj);
+ delete obj;
+ }
}
}
}
// This won't work if proper generation is disabled
if(m_chunksize == 0)
return WATER_LEVEL+2;
- double level = base_rock_level_2d(m_seed, p2d);
+ double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
return (s16)level;
}
u32 block_count = 0;
{ //sectorlock
- JMutexAutoLock lock(m_sector_mutex);
+ //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
for(; i.atEnd() == false; i++)
dstream<<DTIME<<"There are "<<list.size()<<" sectors."<<std::endl;
- JMutexAutoLock lock(m_sector_mutex);
+ //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
s32 counter = 0;
s32 printed_counter = -100000;
MapSector *sector = NULL;
- JMutexAutoLock lock(m_sector_mutex);
+ //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
try{
sector = loadSectorMeta(sectorsubdir);
ClientMapSector *sector = new ClientMapSector(this, p2d);
{
- JMutexAutoLock lock(m_sector_mutex);
+ //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
m_sectors.insert(p2d, sector);
}
DSTACK(__FUNCTION_NAME);
ClientMapSector *sector = NULL;
- JMutexAutoLock lock(m_sector_mutex);
+ //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
{
sector = new ClientMapSector(this, p2d);
{
- JMutexAutoLock lock(m_sector_mutex);
+ //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
m_sectors.insert(p2d, sector);
}
}
void removeNodeMetadata(v3s16 p);
void nodeMetadataStep(float dtime,
core::map<v3s16, MapBlock*> &changed_blocks);
+
+ /*
+ Misc.
+ */
+ core::map<v2s16, MapSector*> *getSectorsPtr(){return &m_sectors;}
/*
Variables
core::map<MapEventReceiver*, bool> m_event_receivers;
- // Mutex is important because on client map is accessed asynchronously
core::map<v2s16, MapSector*> m_sectors;
- JMutex m_sector_mutex;
+ //JMutex m_sector_mutex;
// Be sure to set this to NULL when the cached sector is deleted
MapSector *m_sector_cache;
v2s16 m_sector_cache_p;
- //WrapperHeightmap m_hwrapper;
-
// Queued transforming water nodes
UniqueQueue<v3s16> m_transforming_liquid;
};
*/
if(version >= 14)
{
- std::ostringstream oss(std::ios_base::binary);
- m_node_metadata.serialize(oss);
- os<<serializeString(oss.str());
+ if(version <= 15)
+ {
+ std::ostringstream oss(std::ios_base::binary);
+ m_node_metadata.serialize(oss);
+ os<<serializeString(oss.str());
+ }
+ else
+ {
+ std::ostringstream oss(std::ios_base::binary);
+ m_node_metadata.serialize(oss);
+ compressZlib(oss.str(), os);
+ //os<<serializeLongString(oss.str());
+ }
}
}
}
{
// Ignore errors
try{
- std::string data = deSerializeString(is);
- std::istringstream iss(data, std::ios_base::binary);
- m_node_metadata.deSerialize(iss);
+ if(version <= 15)
+ {
+ std::string data = deSerializeString(is);
+ std::istringstream iss(data, std::ios_base::binary);
+ m_node_metadata.deSerialize(iss);
+ }
+ else
+ {
+ //std::string data = deSerializeLongString(is);
+ std::ostringstream oss(std::ios_base::binary);
+ decompressZlib(is, oss);
+ std::istringstream iss(oss.str(), std::ios_base::binary);
+ m_node_metadata.deSerialize(iss);
+ }
}
catch(SerializationError &e)
{
in_water_stable(false),
swimming_up(false),
craftresult_is_preview(true),
+ hp(20),
peer_id(PEER_ID_INEXISTENT),
m_pitch(0),
m_yaw(0),
args.setFloat("yaw", m_yaw);
args.setV3F("position", m_position);
args.setBool("craftresult_is_preview", craftresult_is_preview);
+ args.setS32("hp", hp);
args.writeLines(os);
}catch(SettingNotFoundException &e){
craftresult_is_preview = true;
}
+ try{
+ hp = args.getS32("hp");
+ }catch(SettingNotFoundException &e){
+ hp = 20;
+ }
inventory.deSerialize(is);
}
{
}
-void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d)
+void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d,
+ core::list<CollisionInfo> *collision_info)
{
v3f position = getPosition();
v3f oldpos = position;
*/
if(other_axes_overlap && main_axis_collides)
{
+ v3f old_speed = m_speed;
+
m_speed -= m_speed.dotProduct(dirs[i]) * dirs[i];
position -= position.dotProduct(dirs[i]) * dirs[i];
position += oldpos.dotProduct(dirs[i]) * dirs[i];
+
+ if(collision_info)
+ {
+ // 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);
+ }
+ }
}
}
setPosition(position);
}
+void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d)
+{
+ move(dtime, map, pos_max_d, NULL);
+}
+
void LocalPlayer::applyControl(float dtime)
{
// Clear stuff
#include "common_irrlicht.h"
#include "inventory.h"
+#include "collision.h"
#define PLAYERNAME_SIZE 20
bool craftresult_is_preview;
+ u16 hp;
+
u16 peer_id;
protected:
return true;
}
+ void move(f32 dtime, Map &map, f32 pos_max_d,
+ core::list<CollisionInfo> *collision_info);
void move(f32 dtime, Map &map, f32 pos_max_d);
void applyControl(float dtime);
}
+void compressZlib(const std::string &data, std::ostream &os)
+{
+ SharedBuffer<u8> databuf((u8*)data.c_str(), data.size());
+ compressZlib(databuf, os);
+}
+
void decompressZlib(std::istream &is, std::ostream &os)
{
z_stream z;
13: (dev) Mapgen v2
14: (dev) NodeMetadata
15: (dev) StaticObjects
+ 16: (dev) larger maximum size of node metadata, and compression
*/
// This represents an uninitialized or invalid format
#define SER_FMT_VER_INVALID 255
// Highest supported serialization version
-#define SER_FMT_VER_HIGHEST 15
+#define SER_FMT_VER_HIGHEST 16
// Lowest supported serialization version
#define SER_FMT_VER_LOWEST 0
#define ser_ver_supported(v) (v >= SER_FMT_VER_LOWEST && v <= SER_FMT_VER_HIGHEST)
+void compressZlib(SharedBuffer<u8> data, std::ostream &os);
+void compressZlib(const std::string &data, std::ostream &os);
+void decompressZlib(std::istream &is, std::ostream &os);
+
void compress(SharedBuffer<u8> data, std::ostream &os, u8 version);
+//void compress(const std::string &data, std::ostream &os, u8 version);
void decompress(std::istream &is, std::ostream &os, u8 version);
/*class Serializable
Server::~Server()
{
+ dstream<<"Server::~Server()"<<std::endl;
+
/*
Send shutdown message
*/
if(client->serialization_version == SER_FMT_VER_INVALID)
continue;
- SendChatMessage(client->peer_id, line);
+ try{
+ SendChatMessage(client->peer_id, line);
+ }
+ catch(con::PeerNotFoundException &e)
+ {}
}
}
/*
Save players
*/
+ dstream<<"Server: Saving players"<<std::endl;
m_env.serializePlayers(m_mapsavedir);
/*
m_emergethread.stop();
dout_server<<"Server: Threads stopped"<<std::endl;
-
- dout_server<<"Server: Saving players"<<std::endl;
- // Save players
- // FIXME: Apparently this does not do anything here
- //m_env.serializePlayers(m_mapsavedir);
}
void Server::step(float dtime)
Step node metadata
*/
{
+ //TimeTaker timer("Step node metadata");
+
JMutexAutoLock envlock(m_env_mutex);
JMutexAutoLock conlock(m_con_mutex);
data[20+3-1] = 0;
player->updateName((const char*)&data[3]);
}*/
-
- // Now answer with a TOCLIENT_INIT
- SharedBuffer<u8> reply(2+1+6+8);
- writeU16(&reply[0], TOCLIENT_INIT);
- writeU8(&reply[2], deployed);
- writeV3S16(&reply[2+1], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS));
- //writeU64(&reply[2+1+6], m_env.getServerMap().getSeed());
-
- // Send as reliable
- m_con.Send(peer_id, 0, reply, true);
+ /*
+ Answer with a TOCLIENT_INIT
+ */
+ {
+ SharedBuffer<u8> reply(2+1+6+8);
+ writeU16(&reply[0], TOCLIENT_INIT);
+ writeU8(&reply[2], deployed);
+ writeV3S16(&reply[2+1], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS));
+ //writeU64(&reply[2+1+6], m_env.getServerMap().getSeed());
+ writeU64(&reply[2+1+6], 0); // no seed
+
+ // Send as reliable
+ m_con.Send(peer_id, 0, reply, true);
+ }
+
+ /*
+ Send complete position information
+ */
+ SendMovePlayer(player);
return;
}
+
if(command == TOSERVER_INIT2)
{
derr_server<<DTIME<<"Server: Got TOSERVER_INIT2 from "
SendPlayerInfos();
// Send inventory to player
+ UpdateCrafting(peer->id);
SendInventory(peer->id);
+
+ // Send HP
+ {
+ Player *player = m_env.getPlayer(peer_id);
+ SendPlayerHP(player);
+ }
// Send time of day
{
// Add to inventory and send inventory
ilist->addItem(item);
+ UpdateCrafting(player->peer_id);
SendInventory(player->peer_id);
}
*/
u8 button = readU8(&data[2]);
u16 id = readS16(&data[3]);
- //u16 item_i = readU16(&data[11]);
+ u16 item_i = readU16(&data[11]);
ServerActiveObject *obj = m_env.getActiveObject(id);
{
// Add to inventory and send inventory
ilist->addItem(item);
+ UpdateCrafting(player->peer_id);
SendInventory(player->peer_id);
// Remove object from environment
obj->m_removed = true;
}
+ else
+ {
+ /*
+ Item cannot be picked up. Punch it instead.
+ */
+
+ ToolItem *titem = NULL;
+ std::string toolname = "";
+
+ InventoryList *mlist = player->inventory.getList("main");
+ if(mlist != NULL)
+ {
+ InventoryItem *item = mlist->getItem(item_i);
+ if(item && (std::string)item->getName() == "ToolItem")
+ {
+ titem = (ToolItem*)item;
+ toolname = titem->getToolName();
+ }
+ }
+
+ u16 wear = obj->punch(toolname);
+
+ if(titem)
+ {
+ bool weared_out = titem->addWear(wear);
+ if(weared_out)
+ mlist->deleteItem(item_i);
+ SendInventory(player->peer_id);
+ }
+ }
}
}
}
player->inventory.addItem("main", item);
// Send inventory
+ UpdateCrafting(player->peer_id);
SendInventory(player->peer_id);
}
}
else
mitem->remove(1);
// Send inventory
+ UpdateCrafting(peer_id);
SendInventory(peer_id);
}
item->remove(dropcount);
// Send inventory
+ UpdateCrafting(peer_id);
SendInventory(peer_id);
}
}
else
{
// Send inventory
+ UpdateCrafting(player->peer_id);
SendInventory(player->peer_id);
}
}
}
}
}
+ else if(command == TOSERVER_DAMAGE)
+ {
+ if(g_settings.getBool("enable_damage"))
+ {
+ std::string datastring((char*)&data[2], datasize-2);
+ std::istringstream is(datastring, std::ios_base::binary);
+ u8 damage = readU8(is);
+ if(player->hp > damage)
+ {
+ player->hp -= damage;
+ }
+ else
+ {
+ player->hp = 0;
+
+ dstream<<"TODO: Server: TOSERVER_HP_DECREMENT: Player dies"
+ <<std::endl;
+
+ v3f pos = findSpawnPos(m_env.getServerMap());
+ player->setPosition(pos);
+ player->hp = 20;
+ SendMovePlayer(player);
+ SendPlayerHP(player);
+
+ //TODO: Throw items around
+ }
+ }
+
+ SendPlayerHP(player);
+ }
else
{
derr_server<<"WARNING: Server::ProcessData(): Ignoring "
{
assert(c->current_player);
// Send inventory
+ UpdateCrafting(c->current_player->peer_id);
SendInventory(c->current_player->peer_id);
return;
}
m_peer_change_queue.push_back(c);
}
+/*
+ Static send methods
+*/
+
+void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
+{
+ DSTACK(__FUNCTION_NAME);
+ std::ostringstream os(std::ios_base::binary);
+
+ writeU16(os, TOCLIENT_HP);
+ writeU8(os, hp);
+
+ // Make data buffer
+ std::string s = os.str();
+ SharedBuffer<u8> data((u8*)s.c_str(), s.size());
+ // Send as reliable
+ con.Send(peer_id, 0, data, true);
+}
+
+/*
+ Non-static send methods
+*/
+
void Server::SendObjectData(float dtime)
{
DSTACK(__FUNCTION_NAME);
assert(player);
/*
- Calculate crafting stuff
+ Serialize it
*/
- if(g_settings.getBool("creative_mode") == false)
+
+ std::ostringstream os;
+ //os.imbue(std::locale("C"));
+
+ player->inventory.serialize(os);
+
+ std::string s = os.str();
+
+ SharedBuffer<u8> data(s.size()+2);
+ writeU16(&data[0], TOCLIENT_INVENTORY);
+ memcpy(&data[2], s.c_str(), s.size());
+
+ // Send as reliable
+ m_con.Send(peer_id, 0, data, true);
+}
+
+void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
+{
+ DSTACK(__FUNCTION_NAME);
+
+ std::ostringstream os(std::ios_base::binary);
+ u8 buf[12];
+
+ // Write command
+ writeU16(buf, TOCLIENT_CHAT_MESSAGE);
+ os.write((char*)buf, 2);
+
+ // Write length
+ writeU16(buf, message.size());
+ os.write((char*)buf, 2);
+
+ // Write string
+ for(u32 i=0; i<message.size(); i++)
{
- InventoryList *clist = player->inventory.getList("craft");
- InventoryList *rlist = player->inventory.getList("craftresult");
+ u16 w = message[i];
+ writeU16(buf, w);
+ os.write((char*)buf, 2);
+ }
+
+ // Make data buffer
+ std::string s = os.str();
+ SharedBuffer<u8> data((u8*)s.c_str(), s.size());
+ // Send as reliable
+ m_con.Send(peer_id, 0, data, true);
+}
- if(rlist->getUsedSlots() == 0)
- player->craftresult_is_preview = true;
+void Server::BroadcastChatMessage(const std::wstring &message)
+{
+ for(core::map<u16, RemoteClient*>::Iterator
+ i = m_clients.getIterator();
+ i.atEnd() == false; i++)
+ {
+ // Get client and check that it is valid
+ RemoteClient *client = i.getNode()->getValue();
+ assert(client->peer_id == i.getNode()->getKey());
+ if(client->serialization_version == SER_FMT_VER_INVALID)
+ continue;
- if(rlist && player->craftresult_is_preview)
- {
- rlist->clearItems();
- }
- if(clist && rlist && player->craftresult_is_preview)
- {
- InventoryItem *items[9];
- for(u16 i=0; i<9; i++)
- {
- items[i] = clist->getItem(i);
- }
-
- bool found = false;
+ SendChatMessage(client->peer_id, message);
+ }
+}
- // Wood
- if(!found)
+void Server::SendPlayerHP(Player *player)
+{
+ SendHP(m_con, player->peer_id, player->hp);
+}
+
+void Server::SendMovePlayer(Player *player)
+{
+ DSTACK(__FUNCTION_NAME);
+ std::ostringstream os(std::ios_base::binary);
+
+ writeU16(os, TOCLIENT_MOVE_PLAYER);
+ writeV3F1000(os, player->getPosition());
+ writeF1000(os, player->getPitch());
+ writeF1000(os, player->getYaw());
+
+ {
+ v3f pos = player->getPosition();
+ f32 pitch = player->getPitch();
+ f32 yaw = player->getYaw();
+ dstream<<"Server sending TOCLIENT_MOVE_PLAYER"
+ <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
+ <<" pitch="<<pitch
+ <<" yaw="<<yaw
+ <<std::endl;
+ }
+
+ // Make data buffer
+ std::string s = os.str();
+ SharedBuffer<u8> data((u8*)s.c_str(), s.size());
+ // Send as reliable
+ m_con.Send(player->peer_id, 0, data, true);
+}
+
+void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
+ core::list<u16> *far_players, float far_d_nodes)
+{
+ float maxd = far_d_nodes*BS;
+ v3f p_f = intToFloat(p, BS);
+
+ // Create packet
+ u32 replysize = 8;
+ SharedBuffer<u8> reply(replysize);
+ writeU16(&reply[0], TOCLIENT_REMOVENODE);
+ writeS16(&reply[2], p.X);
+ writeS16(&reply[4], p.Y);
+ writeS16(&reply[6], p.Z);
+
+ for(core::map<u16, RemoteClient*>::Iterator
+ i = m_clients.getIterator();
+ i.atEnd() == false; i++)
+ {
+ // Get client and check that it is valid
+ RemoteClient *client = i.getNode()->getValue();
+ assert(client->peer_id == i.getNode()->getKey());
+ if(client->serialization_version == SER_FMT_VER_INVALID)
+ continue;
+
+ // Don't send if it's the same one
+ if(client->peer_id == ignore_id)
+ continue;
+
+ if(far_players)
+ {
+ // Get player
+ Player *player = m_env.getPlayer(client->peer_id);
+ if(player)
{
- ItemSpec specs[9];
- specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_TREE);
- if(checkItemCombination(items, specs))
+ // If player is far away, only set modified blocks not sent
+ v3f player_pos = player->getPosition();
+ if(player_pos.getDistanceFrom(p_f) > maxd)
{
- rlist->addItem(new MaterialItem(CONTENT_WOOD, 4));
- found = true;
+ far_players->push_back(client->peer_id);
+ continue;
}
}
+ }
- // Stick
- if(!found)
+ // Send as reliable
+ m_con.Send(client->peer_id, 0, reply, true);
+ }
+}
+
+void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
+ core::list<u16> *far_players, float far_d_nodes)
+{
+ float maxd = far_d_nodes*BS;
+ v3f p_f = intToFloat(p, BS);
+
+ for(core::map<u16, RemoteClient*>::Iterator
+ i = m_clients.getIterator();
+ i.atEnd() == false; i++)
+ {
+ // Get client and check that it is valid
+ RemoteClient *client = i.getNode()->getValue();
+ assert(client->peer_id == i.getNode()->getKey());
+ if(client->serialization_version == SER_FMT_VER_INVALID)
+ continue;
+
+ // Don't send if it's the same one
+ if(client->peer_id == ignore_id)
+ continue;
+
+ if(far_players)
+ {
+ // Get player
+ Player *player = m_env.getPlayer(client->peer_id);
+ if(player)
{
- ItemSpec specs[9];
- specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
- if(checkItemCombination(items, specs))
+ // If player is far away, only set modified blocks not sent
+ v3f player_pos = player->getPosition();
+ if(player_pos.getDistanceFrom(p_f) > maxd)
{
- rlist->addItem(new CraftItem("Stick", 4));
- found = true;
+ far_players->push_back(client->peer_id);
+ continue;
+ }
+ }
+ }
+
+ // Create packet
+ u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
+ SharedBuffer<u8> reply(replysize);
+ writeU16(&reply[0], TOCLIENT_ADDNODE);
+ writeS16(&reply[2], p.X);
+ writeS16(&reply[4], p.Y);
+ writeS16(&reply[6], p.Z);
+ n.serialize(&reply[8], client->serialization_version);
+
+ // Send as reliable
+ m_con.Send(client->peer_id, 0, reply, true);
+ }
+}
+
+void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
+{
+ DSTACK(__FUNCTION_NAME);
+ /*
+ Create a packet with the block in the right format
+ */
+
+ std::ostringstream os(std::ios_base::binary);
+ block->serialize(os, ver);
+ std::string s = os.str();
+ SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
+
+ u32 replysize = 8 + blockdata.getSize();
+ SharedBuffer<u8> reply(replysize);
+ v3s16 p = block->getPos();
+ writeU16(&reply[0], TOCLIENT_BLOCKDATA);
+ writeS16(&reply[2], p.X);
+ writeS16(&reply[4], p.Y);
+ writeS16(&reply[6], p.Z);
+ memcpy(&reply[8], *blockdata, blockdata.getSize());
+
+ /*dstream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
+ <<": \tpacket size: "<<replysize<<std::endl;*/
+
+ /*
+ Send packet
+ */
+ m_con.Send(peer_id, 1, reply, true);
+}
+
+void Server::SendBlocks(float dtime)
+{
+ DSTACK(__FUNCTION_NAME);
+
+ JMutexAutoLock envlock(m_env_mutex);
+ JMutexAutoLock conlock(m_con_mutex);
+
+ //TimeTaker timer("Server::SendBlocks");
+
+ core::array<PrioritySortedBlockTransfer> queue;
+
+ s32 total_sending = 0;
+
+ for(core::map<u16, RemoteClient*>::Iterator
+ i = m_clients.getIterator();
+ i.atEnd() == false; i++)
+ {
+ RemoteClient *client = i.getNode()->getValue();
+ assert(client->peer_id == i.getNode()->getKey());
+
+ total_sending += client->SendingCount();
+
+ if(client->serialization_version == SER_FMT_VER_INVALID)
+ continue;
+
+ client->GetNextBlocks(this, dtime, queue);
+ }
+
+ // Sort.
+ // Lowest priority number comes first.
+ // Lowest is most important.
+ queue.sort();
+
+ for(u32 i=0; i<queue.size(); i++)
+ {
+ //TODO: Calculate limit dynamically
+ if(total_sending >= g_settings.getS32
+ ("max_simultaneous_block_sends_server_total"))
+ break;
+
+ PrioritySortedBlockTransfer q = queue[i];
+
+ MapBlock *block = NULL;
+ try
+ {
+ block = m_env.getMap().getBlockNoCreate(q.pos);
+ }
+ catch(InvalidPositionException &e)
+ {
+ continue;
+ }
+
+ RemoteClient *client = getClient(q.peer_id);
+
+ SendBlockNoLock(q.peer_id, block, client->serialization_version);
+
+ client->SentBlock(q.pos);
+
+ total_sending++;
+ }
+}
+
+/*
+ Something random
+*/
+
+void Server::UpdateCrafting(u16 peer_id)
+{
+ DSTACK(__FUNCTION_NAME);
+
+ Player* player = m_env.getPlayer(peer_id);
+ assert(player);
+
+ /*
+ Calculate crafting stuff
+ */
+ if(g_settings.getBool("creative_mode") == false)
+ {
+ InventoryList *clist = player->inventory.getList("craft");
+ InventoryList *rlist = player->inventory.getList("craftresult");
+
+ if(rlist->getUsedSlots() == 0)
+ player->craftresult_is_preview = true;
+
+ if(rlist && player->craftresult_is_preview)
+ {
+ rlist->clearItems();
+ }
+ if(clist && rlist && player->craftresult_is_preview)
+ {
+ InventoryItem *items[9];
+ for(u16 i=0; i<9; i++)
+ {
+ items[i] = clist->getItem(i);
+ }
+
+ bool found = false;
+
+ // Wood
+ if(!found)
+ {
+ ItemSpec specs[9];
+ specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_TREE);
+ if(checkItemCombination(items, specs))
+ {
+ rlist->addItem(new MaterialItem(CONTENT_WOOD, 4));
+ found = true;
+ }
+ }
+
+ // Stick
+ if(!found)
+ {
+ ItemSpec specs[9];
+ specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
+ if(checkItemCombination(items, specs))
+ {
+ rlist->addItem(new CraftItem("Stick", 4));
+ found = true;
}
}
}
}
- // Wooden showel
+ // Wooden shovel
if(!found)
{
ItemSpec specs[9];
}
}
- // Stone showel
+ // Stone shovel
if(!found)
{
ItemSpec specs[9];
}
}
- // Steel showel
+ // Steel shovel
if(!found)
{
ItemSpec specs[9];
}
}
+ // Wooden sword
+ if(!found)
+ {
+ ItemSpec specs[9];
+ specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
+ specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
+ specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
+ if(checkItemCombination(items, specs))
+ {
+ rlist->addItem(new ToolItem("WSword", 0));
+ found = true;
+ }
+ }
+
+ // Stone sword
+ if(!found)
+ {
+ ItemSpec specs[9];
+ specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
+ specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
+ specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
+ if(checkItemCombination(items, specs))
+ {
+ rlist->addItem(new ToolItem("STSword", 0));
+ found = true;
+ }
+ }
+
+ // Steel sword
+ if(!found)
+ {
+ ItemSpec specs[9];
+ specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
+ specs[4] = ItemSpec(ITEM_CRAFT, "steel_ingot");
+ specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
+ if(checkItemCombination(items, specs))
+ {
+ rlist->addItem(new ToolItem("SteelSword", 0));
+ found = true;
+ }
+ }
+
// Chest
if(!found)
{
}
} // if creative_mode == false
-
- /*
- Serialize it
- */
-
- std::ostringstream os;
- //os.imbue(std::locale("C"));
-
- player->inventory.serialize(os);
-
- std::string s = os.str();
-
- SharedBuffer<u8> data(s.size()+2);
- writeU16(&data[0], TOCLIENT_INVENTORY);
- memcpy(&data[2], s.c_str(), s.size());
-
- // Send as reliable
- m_con.Send(peer_id, 0, data, true);
-}
-
-void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
-{
- DSTACK(__FUNCTION_NAME);
-
- std::ostringstream os(std::ios_base::binary);
- u8 buf[12];
-
- // Write command
- writeU16(buf, TOCLIENT_CHAT_MESSAGE);
- os.write((char*)buf, 2);
-
- // Write length
- writeU16(buf, message.size());
- os.write((char*)buf, 2);
-
- // Write string
- for(u32 i=0; i<message.size(); i++)
- {
- u16 w = message[i];
- writeU16(buf, w);
- os.write((char*)buf, 2);
- }
-
- // Make data buffer
- std::string s = os.str();
- SharedBuffer<u8> data((u8*)s.c_str(), s.size());
- // Send as reliable
- m_con.Send(peer_id, 0, data, true);
}
-void Server::BroadcastChatMessage(const std::wstring &message)
-{
- for(core::map<u16, RemoteClient*>::Iterator
- i = m_clients.getIterator();
- i.atEnd() == false; i++)
- {
- // Get client and check that it is valid
- RemoteClient *client = i.getNode()->getValue();
- assert(client->peer_id == i.getNode()->getKey());
- if(client->serialization_version == SER_FMT_VER_INVALID)
- continue;
-
- SendChatMessage(client->peer_id, message);
- }
-}
-
-void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
- core::list<u16> *far_players, float far_d_nodes)
-{
- float maxd = far_d_nodes*BS;
- v3f p_f = intToFloat(p, BS);
-
- // Create packet
- u32 replysize = 8;
- SharedBuffer<u8> reply(replysize);
- writeU16(&reply[0], TOCLIENT_REMOVENODE);
- writeS16(&reply[2], p.X);
- writeS16(&reply[4], p.Y);
- writeS16(&reply[6], p.Z);
-
- for(core::map<u16, RemoteClient*>::Iterator
- i = m_clients.getIterator();
- i.atEnd() == false; i++)
- {
- // Get client and check that it is valid
- RemoteClient *client = i.getNode()->getValue();
- assert(client->peer_id == i.getNode()->getKey());
- if(client->serialization_version == SER_FMT_VER_INVALID)
- continue;
-
- // Don't send if it's the same one
- if(client->peer_id == ignore_id)
- continue;
-
- if(far_players)
- {
- // Get player
- Player *player = m_env.getPlayer(client->peer_id);
- if(player)
- {
- // If player is far away, only set modified blocks not sent
- v3f player_pos = player->getPosition();
- if(player_pos.getDistanceFrom(p_f) > maxd)
- {
- far_players->push_back(client->peer_id);
- continue;
- }
- }
- }
-
- // Send as reliable
- m_con.Send(client->peer_id, 0, reply, true);
- }
-}
-
-void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
- core::list<u16> *far_players, float far_d_nodes)
-{
- float maxd = far_d_nodes*BS;
- v3f p_f = intToFloat(p, BS);
-
- for(core::map<u16, RemoteClient*>::Iterator
- i = m_clients.getIterator();
- i.atEnd() == false; i++)
- {
- // Get client and check that it is valid
- RemoteClient *client = i.getNode()->getValue();
- assert(client->peer_id == i.getNode()->getKey());
- if(client->serialization_version == SER_FMT_VER_INVALID)
- continue;
-
- // Don't send if it's the same one
- if(client->peer_id == ignore_id)
- continue;
-
- if(far_players)
- {
- // Get player
- Player *player = m_env.getPlayer(client->peer_id);
- if(player)
- {
- // If player is far away, only set modified blocks not sent
- v3f player_pos = player->getPosition();
- if(player_pos.getDistanceFrom(p_f) > maxd)
- {
- far_players->push_back(client->peer_id);
- continue;
- }
- }
- }
-
- // Create packet
- u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
- SharedBuffer<u8> reply(replysize);
- writeU16(&reply[0], TOCLIENT_ADDNODE);
- writeS16(&reply[2], p.X);
- writeS16(&reply[4], p.Y);
- writeS16(&reply[6], p.Z);
- n.serialize(&reply[8], client->serialization_version);
-
- // Send as reliable
- m_con.Send(client->peer_id, 0, reply, true);
- }
-}
-
-void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
-{
- DSTACK(__FUNCTION_NAME);
- /*
- Create a packet with the block in the right format
- */
-
- std::ostringstream os(std::ios_base::binary);
- block->serialize(os, ver);
- std::string s = os.str();
- SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
-
- u32 replysize = 8 + blockdata.getSize();
- SharedBuffer<u8> reply(replysize);
- v3s16 p = block->getPos();
- writeU16(&reply[0], TOCLIENT_BLOCKDATA);
- writeS16(&reply[2], p.X);
- writeS16(&reply[4], p.Y);
- writeS16(&reply[6], p.Z);
- memcpy(&reply[8], *blockdata, blockdata.getSize());
-
- /*dstream<<"Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
- <<": \tpacket size: "<<replysize<<std::endl;*/
-
- /*
- Send packet
- */
- m_con.Send(peer_id, 1, reply, true);
-}
-
-void Server::SendBlocks(float dtime)
-{
- DSTACK(__FUNCTION_NAME);
-
- JMutexAutoLock envlock(m_env_mutex);
- JMutexAutoLock conlock(m_con_mutex);
-
- //TimeTaker timer("Server::SendBlocks");
-
- core::array<PrioritySortedBlockTransfer> queue;
-
- s32 total_sending = 0;
-
- for(core::map<u16, RemoteClient*>::Iterator
- i = m_clients.getIterator();
- i.atEnd() == false; i++)
- {
- RemoteClient *client = i.getNode()->getValue();
- assert(client->peer_id == i.getNode()->getKey());
-
- total_sending += client->SendingCount();
-
- if(client->serialization_version == SER_FMT_VER_INVALID)
- continue;
-
- client->GetNextBlocks(this, dtime, queue);
- }
-
- // Sort.
- // Lowest priority number comes first.
- // Lowest is most important.
- queue.sort();
-
- for(u32 i=0; i<queue.size(); i++)
- {
- //TODO: Calculate limit dynamically
- if(total_sending >= g_settings.getS32
- ("max_simultaneous_block_sends_server_total"))
- break;
-
- PrioritySortedBlockTransfer q = queue[i];
-
- MapBlock *block = NULL;
- try
- {
- block = m_env.getMap().getBlockNoCreate(q.pos);
- }
- catch(InvalidPositionException &e)
- {
- continue;
- }
-
- RemoteClient *client = getClient(q.peer_id);
-
- SendBlockNoLock(q.peer_id, block, client->serialization_version);
-
- client->SentBlock(q.pos);
-
- total_sending++;
- }
-}
-
-
RemoteClient* Server::getClient(u16 peer_id)
{
DSTACK(__FUNCTION_NAME);
{
player->resetInventory();
- // Give some good picks
+ // Give some good tools
{
- InventoryItem *item = new ToolItem("STPick", 0);
+ InventoryItem *item = new ToolItem("MesePick", 0);
void* r = player->inventory.addItem("main", item);
assert(r == NULL);
}
{
- InventoryItem *item = new ToolItem("MesePick", 0);
+ InventoryItem *item = new ToolItem("SteelPick", 0);
+ void* r = player->inventory.addItem("main", item);
+ assert(r == NULL);
+ }
+ {
+ InventoryItem *item = new ToolItem("SteelAxe", 0);
+ void* r = player->inventory.addItem("main", item);
+ assert(r == NULL);
+ }
+ {
+ InventoryItem *item = new ToolItem("SteelShovel", 0);
void* r = player->inventory.addItem("main", item);
assert(r == NULL);
}
}*/
}
+v3f findSpawnPos(ServerMap &map)
+{
+ v2s16 nodepos;
+ s16 groundheight = 0;
+
+ // Try to find a good place a few times
+ for(s32 i=0; i<1000; i++)
+ {
+ s32 range = 1 + i;
+ // We're going to try to throw the player to this position
+ nodepos = v2s16(-range + (myrand()%(range*2)),
+ -range + (myrand()%(range*2)));
+ v2s16 sectorpos = getNodeSectorPos(nodepos);
+ // Get sector (NOTE: Don't get because it's slow)
+ //m_env.getMap().emergeSector(sectorpos);
+ // Get ground height at point (fallbacks to heightmap function)
+ groundheight = map.findGroundLevel(nodepos);
+ // Don't go underwater
+ if(groundheight < WATER_LEVEL)
+ {
+ //dstream<<"-> Underwater"<<std::endl;
+ continue;
+ }
+ // Don't go to high places
+ if(groundheight > WATER_LEVEL + 4)
+ {
+ //dstream<<"-> Underwater"<<std::endl;
+ continue;
+ }
+
+ // Found a good place
+ //dstream<<"Searched through "<<i<<" places."<<std::endl;
+ break;
+ }
+
+ // If no suitable place was not found, go above water at least.
+ if(groundheight < WATER_LEVEL)
+ groundheight = WATER_LEVEL;
+
+ return intToFloat(v3s16(
+ nodepos.X,
+ groundheight + 2,
+ nodepos.Y
+ ), BS);
+}
+
Player *Server::emergePlayer(const char *name, const char *password,
u16 peer_id)
{
dstream<<"Server: Finding spawn place for player \""
<<player->getName()<<"\""<<std::endl;
- v2s16 nodepos;
-#if 0
- player->setPosition(intToFloat(v3s16(
- 0,
- 45, //64,
- 0
- ), BS));
-#endif
-#if 1
- s16 groundheight = 0;
-#if 1
- // Try to find a good place a few times
- for(s32 i=0; i<1000; i++)
- {
- s32 range = 1 + i;
- // We're going to try to throw the player to this position
- nodepos = v2s16(-range + (myrand()%(range*2)),
- -range + (myrand()%(range*2)));
- v2s16 sectorpos = getNodeSectorPos(nodepos);
- // Get sector (NOTE: Don't get because it's slow)
- //m_env.getMap().emergeSector(sectorpos);
- // Get ground height at point (fallbacks to heightmap function)
- groundheight = m_env.getServerMap().findGroundLevel(nodepos);
- // Don't go underwater
- if(groundheight < WATER_LEVEL)
- {
- //dstream<<"-> Underwater"<<std::endl;
- continue;
- }
- // Don't go to high places
- if(groundheight > WATER_LEVEL + 4)
- {
- //dstream<<"-> Underwater"<<std::endl;
- continue;
- }
+ v3f pos = findSpawnPos(m_env.getServerMap());
- // Found a good place
- dstream<<"Searched through "<<i<<" places."<<std::endl;
- break;
- }
-#endif
-
- // If no suitable place was not found, go above water at least.
- if(groundheight < WATER_LEVEL)
- groundheight = WATER_LEVEL;
-
- player->setPosition(intToFloat(v3s16(
- nodepos.X,
- groundheight + 5, // Accomodate mud
- nodepos.Y
- ), BS));
-#endif
+ player->setPosition(pos);
/*
Add player to environment
#include "map.h"
#include "inventory.h"
+/*
+ Some random functions
+*/
+v3f findSpawnPos(ServerMap &map);
+
+/*
+ A structure containing the data needed for queueing the fetching
+ of blocks.
+*/
struct QueuedBlockEmerge
{
v3s16 pos;
void peerAdded(con::Peer *peer);
void deletingPeer(con::Peer *peer, bool timeout);
+ /*
+ Static send methods
+ */
+
+ static void SendHP(con::Connection &con, u16 peer_id, u8 hp);
+
+ /*
+ Non-static send methods
+ */
+
// Envlock and conlock should be locked when calling these
void SendObjectData(float dtime);
void SendPlayerInfos();
void SendInventory(u16 peer_id);
void SendChatMessage(u16 peer_id, const std::wstring &message);
void BroadcastChatMessage(const std::wstring &message);
+ void SendPlayerHP(Player *player);
+ void SendMovePlayer(Player *player);
/*
Send a node removal/addition event to all clients except ignore_id.
Additionally, if far_players!=NULL, players further away than
// Sends blocks to clients
void SendBlocks(float dtime);
+
+ /*
+ Something random
+ */
+
+ void UpdateCrafting(u16 peer_id);
// When called, connection mutex should be locked
RemoteClient* getClient(u16 peer_id);
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 > 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;
+ 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;
+}
+
*/
virtual InventoryItem* createPickedUpItem(){return NULL;}
+ /*
+ If the object doesn't return an item, this will be called.
+ Return value is tool wear.
+ */
+ virtual u16 punch(const std::string &toolname){return 0;}
+
// Number of players which know about this object
u16 m_known_by_count;
/*
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
assert(got_exception);
}
{
- //u8 data1[1100];
- SharedBuffer<u8> data1(1100);
- for(u16 i=0; i<1100; i++){
+ const int datasize = 30000;
+ SharedBuffer<u8> data1(datasize);
+ for(u16 i=0; i<datasize; i++){
data1[i] = i/4;
}
- dstream<<"Sending data (size="<<1100<<"):";
- for(int i=0; i<1100 && i<20; i++){
+ dstream<<"Sending data (size="<<datasize<<"):";
+ for(int i=0; i<datasize && i<20; i++){
if(i%2==0) DEBUGPRINT(" ");
DEBUGPRINT("%.2X", ((int)((const char*)*data1)[i])&0xff);
}
- if(1100>20)
+ if(datasize>20)
dstream<<"...";
dstream<<std::endl;
sleep_ms(50);
- u8 recvdata[2000];
+ u8 recvdata[datasize + 1000];
dstream<<"** running client.Receive()"<<std::endl;
u16 peer_id = 132;
- u16 size = client.Receive(peer_id, recvdata, 2000);
+ u16 size = client.Receive(peer_id, recvdata, datasize + 1000);
dstream<<"** Client received: peer_id="<<peer_id
<<", size="<<size
<<std::endl;
}
inline u16 readU16(std::istream &is)
{
- char buf[12];
- is.read(buf, 12);
+ char buf[2];
+ is.read(buf, 2);
return readU16((u8*)buf);
}
}
inline f32 readF1000(std::istream &is)
{
- char buf[12];
- is.read(buf, 12);
+ char buf[2];
+ is.read(buf, 2);
return readF1000((u8*)buf);
}