#include "content_abm.h"
#include "content_sao.h"
#include "emerge.h"
+#include "mapblock.h"
#include "nodedef.h"
#include "player.h"
#include "rollback_interface.h"
-#include "scripting_game.h"
+#include "scripting_server.h"
#include "settings.h"
#include "tool.h"
#include "version.h"
u16 protocol_version = m_clients.getProtocolVersion(pkt->getPeerId());
- ///// begin compatibility code
- PlayerSAO* playersao = NULL;
- if (protocol_version <= 22) {
- playersao = StageTwoClientInit(pkt->getPeerId());
-
- if (playersao == NULL) {
- actionstream
- << "TOSERVER_INIT2 stage 2 client init failed for peer "
- << pkt->getPeerId() << std::endl;
- return;
- }
- }
- ///// end compatibility code
-
/*
Send some initialization data
*/
float time_speed = g_settings->getFloat("time_speed");
SendTimeOfDay(pkt->getPeerId(), time, time_speed);
- ///// begin compatibility code
- if (protocol_version <= 22) {
- m_clients.event(pkt->getPeerId(), CSE_SetClientReady);
- m_script->on_joinplayer(playersao);
- }
- ///// end compatibility code
-
// Warnings about protocol version can be issued here
if (getClient(pkt->getPeerId())->net_proto_version < LATEST_PROTOCOL_VERSION) {
SendChatMessage(pkt->getPeerId(), L"# Server: WARNING: YOUR CLIENT'S "
sendRequestedMedia(pkt->getPeerId(), tosend);
}
-void Server::handleCommand_ReceivedMedia(NetworkPacket* pkt)
-{
-}
-
void Server::handleCommand_ClientReady(NetworkPacket* pkt)
{
u16 peer_id = pkt->getPeerId();
m_clients.event(peer_id, CSE_SetClientReady);
m_script->on_joinplayer(playersao);
+ // Send shutdown timer if shutdown has been scheduled
+ if (m_shutdown_timer > 0.0f) {
+ std::wstringstream ws;
+ ws << L"*** Server shutting down in "
+ << duration_to_string(myround(m_shutdown_timer)).c_str() << ".";
+ SendChatMessage(pkt->getPeerId(), ws.str());
+ }
}
void Server::handleCommand_GotBlocks(NetworkPacket* pkt)
}
}
-void Server::handleCommand_PlayerPos(NetworkPacket* pkt)
+void Server::process_PlayerPos(RemotePlayer *player, PlayerSAO *playersao,
+ NetworkPacket *pkt)
{
- if (pkt->getSize() < 12 + 12 + 4 + 4)
+ if (pkt->getRemainingBytes() < 12 + 12 + 4 + 4)
return;
v3s32 ps, ss;
s32 f32pitch, f32yaw;
+ u8 f32fov;
*pkt >> ps;
*pkt >> ss;
f32 yaw = (f32)f32yaw / 100.0;
u32 keyPressed = 0;
- if (pkt->getSize() >= 12 + 12 + 4 + 4 + 4)
+ // default behavior (in case an old client doesn't send these)
+ f32 fov = 0;
+ u8 wanted_range = 0;
+
+ if (pkt->getRemainingBytes() >= 4)
*pkt >> keyPressed;
+ if (pkt->getRemainingBytes() >= 1) {
+ *pkt >> f32fov;
+ fov = (f32)f32fov / 80.0;
+ }
+ if (pkt->getRemainingBytes() >= 1)
+ *pkt >> wanted_range;
v3f position((f32)ps.X / 100.0, (f32)ps.Y / 100.0, (f32)ps.Z / 100.0);
v3f speed((f32)ss.X / 100.0, (f32)ss.Y / 100.0, (f32)ss.Z / 100.0);
pitch = modulo360f(pitch);
- yaw = modulo360f(yaw);
+ yaw = wrapDegrees_0_360(yaw);
+
+ playersao->setBasePosition(position);
+ player->setSpeed(speed);
+ playersao->setPitch(pitch);
+ playersao->setYaw(yaw);
+ playersao->setFov(fov);
+ playersao->setWantedRange(wanted_range);
+ player->keyPressed = keyPressed;
+ player->control.up = (keyPressed & 1);
+ player->control.down = (keyPressed & 2);
+ player->control.left = (keyPressed & 4);
+ player->control.right = (keyPressed & 8);
+ player->control.jump = (keyPressed & 16);
+ player->control.aux1 = (keyPressed & 32);
+ player->control.sneak = (keyPressed & 64);
+ player->control.LMB = (keyPressed & 128);
+ player->control.RMB = (keyPressed & 256);
+
+ if (playersao->checkMovementCheat()) {
+ // Call callbacks
+ m_script->on_cheat(playersao, "moved_too_fast");
+ SendMovePlayer(pkt->getPeerId());
+ }
+}
- Player *player = m_env->getPlayer(pkt->getPeerId());
+void Server::handleCommand_PlayerPos(NetworkPacket* pkt)
+{
+ RemotePlayer *player = m_env->getPlayer(pkt->getPeerId());
if (player == NULL) {
errorstream << "Server::ProcessData(): Canceling: "
"No player for peer_id=" << pkt->getPeerId()
return;
}
- // If player is dead we don't care of this packet
- if (player->isDead()) {
- verbosestream << "TOSERVER_PLAYERPOS: " << player->getName()
- << " is dead. Ignoring packet";
- return;
- }
-
PlayerSAO *playersao = player->getPlayerSAO();
if (playersao == NULL) {
errorstream << "Server::ProcessData(): Canceling: "
return;
}
- player->setPosition(position);
- player->setSpeed(speed);
- player->setPitch(pitch);
- player->setYaw(yaw);
- player->keyPressed = keyPressed;
- player->control.up = (keyPressed & 1);
- player->control.down = (keyPressed & 2);
- player->control.left = (keyPressed & 4);
- player->control.right = (keyPressed & 8);
- player->control.jump = (keyPressed & 16);
- player->control.aux1 = (keyPressed & 32);
- player->control.sneak = (keyPressed & 64);
- player->control.LMB = (keyPressed & 128);
- player->control.RMB = (keyPressed & 256);
-
- if (playersao->checkMovementCheat()) {
- // Call callbacks
- m_script->on_cheat(playersao, "moved_too_fast");
- SendMovePlayer(pkt->getPeerId());
+ // If player is dead we don't care of this packet
+ if (playersao->isDead()) {
+ verbosestream << "TOSERVER_PLAYERPOS: " << player->getName()
+ << " is dead. Ignoring packet";
+ return;
}
+
+ process_PlayerPos(player, playersao, pkt);
}
void Server::handleCommand_DeletedBlocks(NetworkPacket* pkt)
void Server::handleCommand_InventoryAction(NetworkPacket* pkt)
{
- Player *player = m_env->getPlayer(pkt->getPeerId());
+ RemotePlayer *player = m_env->getPlayer(pkt->getPeerId());
+
if (player == NULL) {
errorstream << "Server::ProcessData(): Canceling: "
"No player for peer_id=" << pkt->getPeerId()
delete a;
return;
}
+
+ // Disallow dropping items if dead
+ if (playersao->isDead()) {
+ infostream << "Ignoring IDropAction from "
+ << (da->from_inv.dump()) << ":" << da->from_list
+ << " because player is dead." << std::endl;
+ delete a;
+ return;
+ }
}
/*
Handle restrictions and special cases of the craft action
message += (wchar_t)tmp_wchar;
}
- Player *player = m_env->getPlayer(pkt->getPeerId());
+ RemotePlayer *player = m_env->getPlayer(pkt->getPeerId());
if (player == NULL) {
errorstream << "Server::ProcessData(): Canceling: "
"No player for peer_id=" << pkt->getPeerId()
*pkt >> damage;
- Player *player = m_env->getPlayer(pkt->getPeerId());
+ RemotePlayer *player = m_env->getPlayer(pkt->getPeerId());
+
if (player == NULL) {
errorstream << "Server::ProcessData(): Canceling: "
"No player for peer_id=" << pkt->getPeerId()
}
if (g_settings->getBool("enable_damage")) {
+ if (playersao->isDead()) {
+ verbosestream << "Server::ProcessData(): Info: "
+ "Ignoring damage as player " << player->getName()
+ << " is already dead." << std::endl;
+ return;
+ }
+
actionstream << player->getName() << " damaged by "
- << (int)damage << " hp at " << PP(player->getPosition() / BS)
+ << (int)damage << " hp at " << PP(playersao->getBasePosition() / BS)
<< std::endl;
playersao->setHP(playersao->getHP() - damage);
}
}
-void Server::handleCommand_Breath(NetworkPacket* pkt)
-{
- u16 breath;
-
- *pkt >> breath;
-
- Player *player = m_env->getPlayer(pkt->getPeerId());
- if (player == NULL) {
- errorstream << "Server::ProcessData(): Canceling: "
- "No player for peer_id=" << pkt->getPeerId()
- << " disconnecting peer!" << std::endl;
- m_con.DisconnectPeer(pkt->getPeerId());
- return;
- }
-
- /*
- * If player is dead, we don't need to update the breath
- * He is dead !
- */
- if (player->isDead()) {
- verbosestream << "TOSERVER_BREATH: " << player->getName()
- << " is dead. Ignoring packet";
- return;
- }
-
-
- PlayerSAO *playersao = player->getPlayerSAO();
- if (playersao == NULL) {
- errorstream << "Server::ProcessData(): Canceling: "
- "No player object for peer_id=" << pkt->getPeerId()
- << " disconnecting peer!" << std::endl;
- m_con.DisconnectPeer(pkt->getPeerId());
- return;
- }
-
- playersao->setBreath(breath);
- SendPlayerBreath(pkt->getPeerId());
-}
-
void Server::handleCommand_Password(NetworkPacket* pkt)
{
if (pkt->getSize() != PASSWORD_SIZE * 2)
newpwd += c;
}
- Player *player = m_env->getPlayer(pkt->getPeerId());
+ RemotePlayer *player = m_env->getPlayer(pkt->getPeerId());
if (player == NULL) {
errorstream << "Server::ProcessData(): Canceling: "
"No player for peer_id=" << pkt->getPeerId()
if (pkt->getSize() < 2)
return;
- Player *player = m_env->getPlayer(pkt->getPeerId());
+ RemotePlayer *player = m_env->getPlayer(pkt->getPeerId());
+
if (player == NULL) {
errorstream << "Server::ProcessData(): Canceling: "
"No player for peer_id=" << pkt->getPeerId()
void Server::handleCommand_Respawn(NetworkPacket* pkt)
{
- Player *player = m_env->getPlayer(pkt->getPeerId());
+ RemotePlayer *player = m_env->getPlayer(pkt->getPeerId());
if (player == NULL) {
errorstream << "Server::ProcessData(): Canceling: "
"No player for peer_id=" << pkt->getPeerId()
return;
}
- if (!player->isDead())
+ PlayerSAO *playersao = player->getPlayerSAO();
+ assert(playersao);
+
+ if (!playersao->isDead())
return;
RespawnPlayer(pkt->getPeerId());
actionstream << player->getName() << " respawns at "
- << PP(player->getPosition()/BS) << std::endl;
+ << PP(playersao->getBasePosition() / BS) << std::endl;
// ActiveObject is added to environment in AsyncRunStep after
// the previous addition has been successfully removed
void Server::handleCommand_Interact(NetworkPacket* pkt)
{
- std::string datastring(pkt->getString(0), pkt->getSize());
- std::istringstream is(datastring, std::ios_base::binary);
-
/*
[0] u16 command
[2] u8 action
[3] u16 item
- [5] u32 length of the next item
+ [5] u32 length of the next item (plen)
[9] serialized PointedThing
+ [9 + plen] player position information
actions:
0: start digging (from undersurface) or use
1: stop digging (all parameters ignored)
2: digging completed
3: place block or item (to abovesurface)
4: use item
+ 5: rightclick air ("activate")
*/
- u8 action = readU8(is);
- u16 item_i = readU16(is);
- std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
+ u8 action;
+ u16 item_i;
+ *pkt >> action;
+ *pkt >> item_i;
+ std::istringstream tmp_is(pkt->readLongString(), std::ios::binary);
PointedThing pointed;
pointed.deSerialize(tmp_is);
verbosestream << "TOSERVER_INTERACT: action=" << (int)action << ", item="
<< item_i << ", pointed=" << pointed.dump() << std::endl;
- Player *player = m_env->getPlayer(pkt->getPeerId());
+ RemotePlayer *player = m_env->getPlayer(pkt->getPeerId());
+
if (player == NULL) {
errorstream << "Server::ProcessData(): Canceling: "
"No player for peer_id=" << pkt->getPeerId()
return;
}
- if (player->isDead()) {
- verbosestream << "TOSERVER_INTERACT: " << player->getName()
- << " is dead. Ignoring packet";
+ if (playersao->isDead()) {
+ actionstream << "Server: NoCheat: " << player->getName()
+ << " tried to interact while dead; ignoring." << std::endl;
+ if (pointed.type == POINTEDTHING_NODE) {
+ // Re-send block to revert change on client-side
+ RemoteClient *client = getClient(pkt->getPeerId());
+ v3s16 blockpos = getNodeBlockPos(pointed.node_undersurface);
+ client->SetBlockNotSent(blockpos);
+ }
+ // Call callbacks
+ m_script->on_cheat(playersao, "interacted_while_dead");
return;
}
+ process_PlayerPos(player, playersao, pkt);
+
v3f player_pos = playersao->getLastGoodPosition();
// Update wielded item
pointed_pos_above = pointed_pos_under;
}
+ /*
+ Make sure the player is allowed to do it
+ */
+ if (!checkPriv(player->getName(), "interact")) {
+ actionstream<<player->getName()<<" attempted to interact with "
+ <<pointed.dump()<<" without 'interact' privilege"
+ <<std::endl;
+ // Re-send block to revert change on client-side
+ RemoteClient *client = getClient(pkt->getPeerId());
+ // Digging completed -> under
+ if (action == 2) {
+ v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
+ client->SetBlockNotSent(blockpos);
+ }
+ // Placement -> above
+ if (action == 3) {
+ v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_above, BS));
+ client->SetBlockNotSent(blockpos);
+ }
+ return;
+ }
+
/*
Check that target is reasonably close
(only when digging or placing things)
*/
static const bool enable_anticheat = !g_settings->getBool("disable_anticheat");
- if ((action == 0 || action == 2 || action == 3) &&
+ if ((action == 0 || action == 2 || action == 3 || action == 4) &&
(enable_anticheat && !isSingleplayer())) {
float d = player_pos.getDistanceFrom(pointed_pos_under);
- float max_d = BS * 14; // Just some large enough value
- if (d > max_d) {
+ const ItemDefinition &playeritem_def =
+ playersao->getWieldedItem().getDefinition(m_itemdef);
+ float max_d = BS * playeritem_def.range;
+ InventoryList *hlist = playersao->getInventory()->getList("hand");
+ const ItemDefinition &hand_def =
+ hlist ? (hlist->getItem(0).getDefinition(m_itemdef)) : (m_itemdef->get(""));
+ float max_d_hand = BS * hand_def.range;
+ if (max_d < 0 && max_d_hand >= 0)
+ max_d = max_d_hand;
+ else if (max_d < 0)
+ max_d = BS * 4.0;
+ // cube diagonal: sqrt(3) = 1.73
+ if (d > max_d * 1.73) {
actionstream << "Player " << player->getName()
<< " tried to access " << pointed.dump()
<< " from too far: "
}
}
- /*
- Make sure the player is allowed to do it
- */
- if (!checkPriv(player->getName(), "interact")) {
- actionstream<<player->getName()<<" attempted to interact with "
- <<pointed.dump()<<" without 'interact' privilege"
- <<std::endl;
- // Re-send block to revert change on client-side
- RemoteClient *client = getClient(pkt->getPeerId());
- // Digging completed -> under
- if (action == 2) {
- v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
- client->SetBlockNotSent(blockpos);
- }
- // Placement -> above
- if (action == 3) {
- v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_above, BS));
- client->SetBlockNotSent(blockpos);
- }
- return;
- }
-
/*
If something goes wrong, this player is to blame
*/
*/
if (action == 0) {
if (pointed.type == POINTEDTHING_NODE) {
- /*
- NOTE: This can be used in the future to check if
- somebody is cheating, by checking the timing.
- */
-
MapNode n(CONTENT_IGNORE);
bool pos_ok;
<<pointed.object_id<<": "
<<pointed_object->getDescription()<<std::endl;
- ItemStack punchitem = playersao->getWieldedItem();
+ ItemStack punchitem = playersao->getWieldedItemOrHand();
ToolCapabilities toolcap =
punchitem.getToolCapabilities(m_itemdef);
v3f dir = (pointed_object->getBasePosition() -
- (player->getPosition() + player->getEyeOffset())
+ (playersao->getBasePosition() + playersao->getEyeOffset())
).normalize();
float time_from_last_punch =
playersao->resetTimeFromLastPunch();
m_script->on_cheat(playersao, "finished_unknown_dig");
}
// Get player's wielded item
- ItemStack playeritem;
- InventoryList *mlist = playersao->getInventory()->getList("main");
- if (mlist != NULL)
- playeritem = mlist->getItem(playersao->getWieldIndex());
+ ItemStack playeritem = playersao->getWieldedItemOrHand();
ToolCapabilities playeritem_toolcap =
playeritem.getToolCapabilities(m_itemdef);
// Get diggability and expected digging time
&playeritem_toolcap);
// If can't dig, try hand
if (!params.diggable) {
- const ItemDefinition &hand = m_itemdef->get("");
+ InventoryList *hlist = playersao->getInventory()->getList("hand");
+ const ItemDefinition &hand =
+ hlist ? hlist->getItem(0).getDefinition(m_itemdef) : m_itemdef->get("");
const ToolCapabilities *tp = hand.tool_capabilities;
if (tp)
params = getDigParams(m_nodedef->get(n).groups, tp);
fields[fieldname] = pkt->readLongString();
}
- Player *player = m_env->getPlayer(pkt->getPeerId());
+ RemotePlayer *player = m_env->getPlayer(pkt->getPeerId());
+
if (player == NULL) {
errorstream << "Server::ProcessData(): Canceling: "
"No player for peer_id=" << pkt->getPeerId()
fields[fieldname] = pkt->readLongString();
}
- Player *player = m_env->getPlayer(pkt->getPeerId());
+ RemotePlayer *player = m_env->getPlayer(pkt->getPeerId());
+
if (player == NULL) {
errorstream << "Server::ProcessData(): Canceling: "
"No player for peer_id=" << pkt->getPeerId()