Handle death and respawn better

This commit is contained in:
Perttu Ahola 2011-10-15 14:46:59 +03:00
parent 857fe0551c
commit 74febd5c31
9 changed files with 476 additions and 89 deletions

@ -152,6 +152,7 @@ set(minetest_SRCS
guiInventoryMenu.cpp guiInventoryMenu.cpp
guiPauseMenu.cpp guiPauseMenu.cpp
guiPasswordChange.cpp guiPasswordChange.cpp
guiDeathScreen.cpp
client.cpp client.cpp
tile.cpp tile.cpp
game.cpp game.cpp

@ -431,7 +431,7 @@ void Client::step(float dtime)
snprintf((char*)&data[23], PASSWORD_SIZE, "%s", m_password.c_str()); snprintf((char*)&data[23], PASSWORD_SIZE, "%s", m_password.c_str());
// This should be incremented in each version // This should be incremented in each version
writeU16(&data[51], 2); writeU16(&data[51], 3);
// Send as unreliable // Send as unreliable
Send(0, data, false); Send(0, data, false);
@ -1477,6 +1477,22 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
} }
} }
} }
else if(command == TOCLIENT_DEATHSCREEN)
{
std::string datastring((char*)&data[2], datasize-2);
std::istringstream is(datastring, std::ios_base::binary);
bool set_camera_point_target = readU8(is);
v3f camera_point_target = readV3F1000(is);
ClientEvent event;
event.type = CE_DEATHSCREEN;
event.deathscreen.set_camera_point_target = set_camera_point_target;
event.deathscreen.camera_point_target_x = camera_point_target.X;
event.deathscreen.camera_point_target_y = camera_point_target.Y;
event.deathscreen.camera_point_target_z = camera_point_target.Z;
m_client_event_queue.push_back(event);
}
else else
{ {
dout_client<<DTIME<<"WARNING: Client: Ignoring unknown command " dout_client<<DTIME<<"WARNING: Client: Ignoring unknown command "
@ -1712,6 +1728,20 @@ void Client::sendDamage(u8 damage)
Send(0, data, true); Send(0, data, true);
} }
void Client::sendRespawn()
{
DSTACK(__FUNCTION_NAME);
std::ostringstream os(std::ios_base::binary);
writeU16(os, TOSERVER_RESPAWN);
// 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() void Client::sendPlayerPos()
{ {
//JMutexAutoLock envlock(m_env_mutex); //bulk comment-out //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out

@ -113,7 +113,8 @@ enum ClientEventType
{ {
CE_NONE, CE_NONE,
CE_PLAYER_DAMAGE, CE_PLAYER_DAMAGE,
CE_PLAYER_FORCE_MOVE CE_PLAYER_FORCE_MOVE,
CE_DEATHSCREEN,
}; };
struct ClientEvent struct ClientEvent
@ -129,6 +130,12 @@ struct ClientEvent
f32 pitch; f32 pitch;
f32 yaw; f32 yaw;
} player_force_move; } player_force_move;
struct{
bool set_camera_point_target;
f32 camera_point_target_x;
f32 camera_point_target_y;
f32 camera_point_target_z;
} deathscreen;
}; };
}; };
@ -191,6 +198,7 @@ public:
void sendChangePassword(const std::wstring oldpassword, void sendChangePassword(const std::wstring oldpassword,
const std::wstring newpassword); const std::wstring newpassword);
void sendDamage(u8 damage); void sendDamage(u8 damage);
void sendRespawn();
// locks envlock // locks envlock
void removeNode(v3s16 p); void removeNode(v3s16 p);

@ -172,6 +172,13 @@ enum ToClientCommand
string serialized item string serialized item
} }
*/ */
TOCLIENT_DEATHSCREEN = 0x37,
/*
u16 command
u8 bool set camera point target
v3f1000 camera point target (to point the death cause or whatever)
*/
}; };
enum ToServerCommand enum ToServerCommand
@ -321,6 +328,10 @@ enum ToServerCommand
[2] u16 item [2] u16 item
*/ */
TOSERVER_RESPAWN=0x38,
/*
u16 TOSERVER_RESPAWN
*/
}; };
inline SharedBuffer<u8> makePacket_TOCLIENT_TIME_OF_DAY(u16 time) inline SharedBuffer<u8> makePacket_TOCLIENT_TIME_OF_DAY(u16 time)

@ -30,6 +30,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "guiPasswordChange.h" #include "guiPasswordChange.h"
#include "guiInventoryMenu.h" #include "guiInventoryMenu.h"
#include "guiTextInputMenu.h" #include "guiTextInputMenu.h"
#include "guiDeathScreen.h"
#include "materials.h" #include "materials.h"
#include "config.h" #include "config.h"
#include "clouds.h" #include "clouds.h"
@ -39,6 +40,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "settings.h" #include "settings.h"
#include "profiler.h" #include "profiler.h"
#include "mainmenumanager.h" #include "mainmenumanager.h"
#include "gettext.h"
/* /*
TODO: Move content-aware stuff to separate file by adding properties TODO: Move content-aware stuff to separate file by adding properties
@ -129,6 +131,26 @@ struct TextDestSignNode : public TextDest
Client *m_client; Client *m_client;
}; };
/* Respawn menu callback */
class MainRespawnInitiator: public IRespawnInitiator
{
public:
MainRespawnInitiator(bool *active, Client *client):
m_active(active), m_client(client)
{
*m_active = true;
}
void respawn()
{
*m_active = false;
m_client->sendRespawn();
}
private:
bool *m_active;
Client *m_client;
};
/* /*
Hotbar draw routine Hotbar draw routine
*/ */
@ -916,6 +938,8 @@ void the_game(
bool invert_mouse = g_settings->getBool("invert_mouse"); bool invert_mouse = g_settings->getBool("invert_mouse");
bool respawn_menu_active = false;
/* /*
Main loop Main loop
*/ */
@ -1388,9 +1412,24 @@ void the_game(
/* /*
Player speed control Player speed control
TODO: Cache the keycodes from getKeySetting
*/ */
if(!noMenuActive() || !device->isWindowActive())
{
PlayerControl control(
false,
false,
false,
false,
false,
false,
false,
camera_pitch,
camera_yaw
);
client.setPlayerControl(control);
}
else
{ {
/*bool a_up, /*bool a_up,
bool a_down, bool a_down,
@ -1435,24 +1474,56 @@ void the_game(
//client.step(dtime_avg1); //client.step(dtime_avg1);
} }
// Read client events
for(;;)
{ {
ClientEvent event = client.getClientEvent(); // Read client events
if(event.type == CE_NONE) for(;;)
{ {
break; ClientEvent event = client.getClientEvent();
} if(event.type == CE_NONE)
else if(event.type == CE_PLAYER_DAMAGE) {
{ break;
//u16 damage = event.player_damage.amount; }
//dstream<<"Player damage: "<<damage<<std::endl; else if(event.type == CE_PLAYER_DAMAGE)
damage_flash_timer = 0.05; {
} //u16 damage = event.player_damage.amount;
else if(event.type == CE_PLAYER_FORCE_MOVE) //dstream<<"Player damage: "<<damage<<std::endl;
{ damage_flash_timer = 0.05;
camera_yaw = event.player_force_move.yaw; if(event.player_damage.amount >= 2){
camera_pitch = event.player_force_move.pitch; damage_flash_timer += 0.05 * event.player_damage.amount;
}
}
else if(event.type == CE_PLAYER_FORCE_MOVE)
{
camera_yaw = event.player_force_move.yaw;
camera_pitch = event.player_force_move.pitch;
}
else if(event.type == CE_DEATHSCREEN)
{
if(respawn_menu_active)
continue;
/*bool set_camera_point_target =
event.deathscreen.set_camera_point_target;
v3f camera_point_target;
camera_point_target.X = event.deathscreen.camera_point_target_x;
camera_point_target.Y = event.deathscreen.camera_point_target_y;
camera_point_target.Z = event.deathscreen.camera_point_target_z;*/
MainRespawnInitiator *respawner =
new MainRespawnInitiator(
&respawn_menu_active, &client);
GUIDeathScreen *menu =
new GUIDeathScreen(guienv, guiroot, -1,
&g_menumgr, respawner);
menu->drop();
/* Handle visualization */
damage_flash_timer = 0;
/*LocalPlayer* player = client.getLocalPlayer();
player->setPosition(player->getPosition() + v3f(0,-BS,0));
camera.update(player, busytime, screensize);*/
}
} }
} }
@ -1468,14 +1539,10 @@ void the_game(
f32 camera_fov = camera.getFovMax(); f32 camera_fov = camera.getFovMax();
if(FIELD_OF_VIEW_TEST) if(FIELD_OF_VIEW_TEST)
{
client.updateCamera(v3f(0,0,0), v3f(0,0,1), camera_fov); client.updateCamera(v3f(0,0,0), v3f(0,0,1), camera_fov);
}
else else
{
client.updateCamera(camera_position, client.updateCamera(camera_position,
camera_direction, camera_fov); camera_direction, camera_fov);
}
//timer2.stop(); //timer2.stop();
//TimeTaker //timer3("//timer3"); //TimeTaker //timer3("//timer3");

179
src/guiDeathScreen.cpp Normal file

@ -0,0 +1,179 @@
/*
Minetest-c55
Copyright (C) 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 "guiDeathScreen.h"
#include "debug.h"
#include "serialization.h"
#include <string>
#include <IGUICheckBox.h>
#include <IGUIEditBox.h>
#include <IGUIButton.h>
#include <IGUIStaticText.h>
#include <IGUIFont.h>
#include "gettext.h"
#include "client.h"
GUIDeathScreen::GUIDeathScreen(gui::IGUIEnvironment* env,
gui::IGUIElement* parent, s32 id,
IMenuManager *menumgr, IRespawnInitiator *respawner
):
GUIModalMenu(env, parent, id, menumgr),
m_respawner(respawner),
m_screensize(1,1)
{
}
GUIDeathScreen::~GUIDeathScreen()
{
removeChildren();
delete m_respawner;
}
void GUIDeathScreen::removeChildren()
{
const core::list<gui::IGUIElement*> &children = getChildren();
core::list<gui::IGUIElement*> children_copy;
for(core::list<gui::IGUIElement*>::ConstIterator
i = children.begin(); i != children.end(); i++)
{
children_copy.push_back(*i);
}
for(core::list<gui::IGUIElement*>::Iterator
i = children_copy.begin();
i != children_copy.end(); i++)
{
(*i)->remove();
}
}
void GUIDeathScreen::regenerateGui(v2u32 screensize)
{
m_screensize = screensize;
/*
Remove stuff
*/
removeChildren();
/*
Calculate new sizes and positions
*/
core::rect<s32> rect(
screensize.X/2 - 500/2,
screensize.Y/2 - 200/2,
screensize.X/2 + 500/2,
screensize.Y/2 + 200/2
);
DesiredRect = rect;
recalculateAbsolutePosition(false);
v2s32 size = rect.getSize();
/*
Add stuff
*/
changeCtype("");
{
core::rect<s32> rect(0, 0, 400, 50);
rect = rect + v2s32(size.X/2-400/2, size.Y/2-50/2-25);
Environment->addStaticText(wgettext("You died."), rect, false,
true, this, 256);
}
{
core::rect<s32> rect(0, 0, 140, 30);
rect = rect + v2s32(size.X/2-140/2, size.Y/2-30/2+25);
gui::IGUIElement *e =
Environment->addButton(rect, this, 257,
wgettext("Respawn"));
Environment->setFocus(e);
}
changeCtype("C");
}
void GUIDeathScreen::drawMenu()
{
gui::IGUISkin* skin = Environment->getSkin();
if (!skin)
return;
video::IVideoDriver* driver = Environment->getVideoDriver();
{
video::SColor color(180,50,0,0);
driver->draw2DRectangle(color,
core::rect<s32>(0,0,m_screensize.X,m_screensize.Y), NULL);
}
{
video::SColor bgcolor(50,0,0,0);
driver->draw2DRectangle(bgcolor, AbsoluteRect, &AbsoluteClippingRect);
}
gui::IGUIElement::draw();
}
bool GUIDeathScreen::OnEvent(const SEvent& event)
{
if(event.EventType==EET_KEY_INPUT_EVENT)
{
if(event.KeyInput.Key==KEY_ESCAPE && event.KeyInput.PressedDown)
{
respawn();
quitMenu();
return true;
}
if(event.KeyInput.Key==KEY_RETURN && event.KeyInput.PressedDown)
{
respawn();
quitMenu();
return true;
}
}
if(event.EventType==EET_GUI_EVENT)
{
if(event.GUIEvent.EventType==gui::EGET_ELEMENT_FOCUS_LOST
&& isVisible())
{
if(!canTakeFocus(event.GUIEvent.Element))
{
dstream<<"GUIDeathScreen: Not allowing focus change."
<<std::endl;
// Returning true disables focus change
return true;
}
}
if(event.GUIEvent.EventType==gui::EGET_BUTTON_CLICKED)
{
switch(event.GUIEvent.Caller->getID())
{
case 257:
respawn();
quitMenu();
return true;
}
}
}
return Parent ? Parent->OnEvent(event) : false;
}
void GUIDeathScreen::respawn()
{
m_respawner->respawn();
}

60
src/guiDeathScreen.h Normal file

@ -0,0 +1,60 @@
/*
Minetest-c55
Copyright (C) 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 GUIMESSAGEMENU_HEADER
#define GUIMESSAGEMENU_HEADER
#include "common_irrlicht.h"
#include "modalMenu.h"
#include "utility.h"
#include <string>
class IRespawnInitiator
{
public:
virtual void respawn() = 0;
};
class GUIDeathScreen : public GUIModalMenu
{
public:
GUIDeathScreen(gui::IGUIEnvironment* env,
gui::IGUIElement* parent, s32 id,
IMenuManager *menumgr, IRespawnInitiator *respawner);
~GUIDeathScreen();
void removeChildren();
/*
Remove and re-add (or reposition) stuff
*/
void regenerateGui(v2u32 screensize);
void drawMenu();
bool OnEvent(const SEvent& event);
void respawn();
private:
IRespawnInitiator *m_respawner;
v2u32 m_screensize;
};
#endif

@ -2026,16 +2026,6 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
// Get player // Get player
Player *player = emergePlayer(playername, password, peer_id); Player *player = emergePlayer(playername, password, peer_id);
/*{
// DEBUG: Test serialization
std::ostringstream test_os;
player->serialize(test_os);
dstream<<"Player serialization test: \""<<test_os.str()
<<"\""<<std::endl;
std::istringstream test_is(test_os.str());
player->deSerialize(test_is);
}*/
// If failed, cancel // If failed, cancel
if(player == NULL) if(player == NULL)
{ {
@ -2044,32 +2034,6 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
return; return;
} }
/*
// If a client is already connected to the player, cancel
if(player->peer_id != 0)
{
derr_server<<DTIME<<"Server: peer_id="<<peer_id
<<" tried to connect to "
"an already connected player (peer_id="
<<player->peer_id<<")"<<std::endl;
return;
}
// Set client of player
player->peer_id = peer_id;
*/
// Check if player doesn't exist
if(player == NULL)
throw con::InvalidIncomingDataException
("Server::ProcessData(): INIT: Player doesn't exist");
/*// update name if it was supplied
if(datasize >= 20+3)
{
data[20+3-1] = 0;
player->updateName((const char*)&data[3]);
}*/
/* /*
Answer with a TOCLIENT_INIT Answer with a TOCLIENT_INIT
*/ */
@ -2146,10 +2110,18 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
} }
// Warnings about protocol version can be issued here // Warnings about protocol version can be issued here
/*if(getClient(peer->id)->net_proto_version == 0) if(getClient(peer->id)->net_proto_version < 3)
{ {
SendChatMessage(peer_id, L"# Server: NOTE: YOUR CLIENT IS OLD AND DOES NOT WORK PROPERLY WITH THIS SERVER"); SendChatMessage(peer_id, L"# Server: WARNING: YOUR CLIENT IS OLD AND DOES NOT WORK PROPERLY WITH THIS SERVER");
}*/ }
/*
Check HP, respawn if necessary
*/
{
Player *player = m_env.getPlayer(peer_id);
HandlePlayerHP(player, 0);
}
return; return;
} }
@ -3208,33 +3180,18 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
} }
else if(command == TOSERVER_DAMAGE) else if(command == TOSERVER_DAMAGE)
{ {
std::string datastring((char*)&data[2], datasize-2);
std::istringstream is(datastring, std::ios_base::binary);
u8 damage = readU8(is);
if(g_settings->getBool("enable_damage")) if(g_settings->getBool("enable_damage"))
{ {
std::string datastring((char*)&data[2], datasize-2); HandlePlayerHP(player, damage);
std::istringstream is(datastring, std::ios_base::binary); }
u8 damage = readU8(is); else
if(player->hp > damage) {
{ SendPlayerHP(player);
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 if(command == TOSERVER_PASSWORD) else if(command == TOSERVER_PASSWORD)
{ {
@ -3296,7 +3253,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
<<std::endl; <<std::endl;
SendChatMessage(peer_id, L"Password change successful"); SendChatMessage(peer_id, L"Password change successful");
} }
else if (command == TOSERVER_PLAYERITEM) else if(command == TOSERVER_PLAYERITEM)
{ {
if (datasize < 2+2) if (datasize < 2+2)
return; return;
@ -3305,6 +3262,13 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
player->wieldItem(item); player->wieldItem(item);
SendWieldedItem(player); SendWieldedItem(player);
} }
else if(command == TOSERVER_RESPAWN)
{
if(player->hp != 0)
return;
RespawnPlayer(player);
}
else else
{ {
derr_server<<"WARNING: Server::ProcessData(): Ignoring " derr_server<<"WARNING: Server::ProcessData(): Ignoring "
@ -3501,6 +3465,23 @@ void Server::SendAccessDenied(con::Connection &con, u16 peer_id,
con.Send(peer_id, 0, data, true); con.Send(peer_id, 0, data, true);
} }
void Server::SendDeathscreen(con::Connection &con, u16 peer_id,
bool set_camera_point_target, v3f camera_point_target)
{
DSTACK(__FUNCTION_NAME);
std::ostringstream os(std::ios_base::binary);
writeU16(os, TOCLIENT_DEATHSCREEN);
writeU8(os, set_camera_point_target);
writeV3F1000(os, camera_point_target);
// 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 Non-static send methods
*/ */
@ -3955,6 +3936,51 @@ void Server::SendBlocks(float dtime)
Something random Something random
*/ */
void Server::HandlePlayerHP(Player *player, s16 damage)
{
if(player->hp > damage)
{
player->hp -= damage;
SendPlayerHP(player);
}
else
{
dstream<<"Server::HandlePlayerHP(): Player "
<<player->getName()<<" dies"<<std::endl;
player->hp = 0;
//TODO: Throw items around
// Handle players that are not connected
if(player->peer_id == PEER_ID_INEXISTENT){
RespawnPlayer(player);
return;
}
SendPlayerHP(player);
RemoteClient *client = getClient(player->peer_id);
if(client->net_proto_version >= 3)
{
SendDeathscreen(m_con, player->peer_id, false, v3f(0,0,0));
}
else
{
RespawnPlayer(player);
}
}
}
void Server::RespawnPlayer(Player *player)
{
v3f pos = findSpawnPos(m_env.getServerMap());
player->setPosition(pos);
player->hp = 20;
SendMovePlayer(player);
SendPlayerHP(player);
}
void Server::UpdateCrafting(u16 peer_id) void Server::UpdateCrafting(u16 peer_id)
{ {
DSTACK(__FUNCTION_NAME); DSTACK(__FUNCTION_NAME);

@ -488,6 +488,8 @@ private:
static void SendHP(con::Connection &con, u16 peer_id, u8 hp); static void SendHP(con::Connection &con, u16 peer_id, u8 hp);
static void SendAccessDenied(con::Connection &con, u16 peer_id, static void SendAccessDenied(con::Connection &con, u16 peer_id,
const std::wstring &reason); const std::wstring &reason);
static void SendDeathscreen(con::Connection &con, u16 peer_id,
bool set_camera_point_target, v3f camera_point_target);
/* /*
Non-static send methods Non-static send methods
@ -526,6 +528,9 @@ private:
Something random Something random
*/ */
void HandlePlayerHP(Player *player, s16 damage);
void RespawnPlayer(Player *player);
void UpdateCrafting(u16 peer_id); void UpdateCrafting(u16 peer_id);
// When called, connection mutex should be locked // When called, connection mutex should be locked