mirror of
https://github.com/ReMinecraftPE/mcpe.git
synced 2025-01-23 09:44:34 -05:00
60b21356a1
* * Add BuildActionIntention crap * * Set Client and World projects to use MP compilation * asd * * Use the new BuildActionIntention to break and place blocks. * * Reverse engineer the IArea system. * * Copy break logic from survival into creative conditionally * * Reverse IBuildInput and MouseHandler * Replace the new relative paths in the client project with $(MC_ROOT) again * * Reverse Multitouch, MouseDevice * * Reverse a bunch of auxiliary classes for input. * * Use CustomInputHolder instead of holding inputs manually. * * Reverse a whole BUNCH of things! * * Add feedback textures to the gitignore. * * D-pad now renders! Also loads of other work. * * More Stuff * * Finish touch control bug fixing. * * Finalize work. * * One last thing.. * * Add a "cramped" mode to the options screen and start menu. * * Oh, forgot to do something
522 lines
13 KiB
C++
522 lines
13 KiB
C++
/********************************************************************
|
|
Minecraft: Pocket Edition - Decompilation Project
|
|
Copyright (C) 2023 iProgramInCpp
|
|
|
|
The following code is licensed under the BSD 1 clause license.
|
|
SPDX-License-Identifier: BSD-1-Clause
|
|
********************************************************************/
|
|
|
|
#include "ServerSideNetworkHandler.hpp"
|
|
#include "common/Utils.hpp"
|
|
|
|
// This lets you make the server shut up and not log events in the debug console.
|
|
#define VERBOSE_SERVER
|
|
|
|
#if defined(ORIGINAL_CODE) || defined(VERBOSE_SERVER)
|
|
#define puts_ignorable(str) LOG_I(str)
|
|
#define printf_ignorable(str, ...) LOG_I(str, __VA_ARGS__)
|
|
#else
|
|
#define puts_ignorable(str)
|
|
#define printf_ignorable(str, ...)
|
|
#endif
|
|
|
|
ServerSideNetworkHandler::ServerSideNetworkHandler(Minecraft* minecraft, RakNetInstance* rakNetInstance)
|
|
{
|
|
m_pMinecraft = minecraft;
|
|
m_pLevel = nullptr;
|
|
m_pRakNetInstance = rakNetInstance;
|
|
allowIncomingConnections(false);
|
|
m_pRakNetPeer = m_pRakNetInstance->getPeer();
|
|
m_bAllowIncoming = false;
|
|
|
|
setupCommands();
|
|
}
|
|
|
|
ServerSideNetworkHandler::~ServerSideNetworkHandler()
|
|
{
|
|
if (m_pLevel)
|
|
m_pLevel->removeListener(this);
|
|
|
|
for (OnlinePlayerMap::iterator it = m_onlinePlayers.begin(); it != m_onlinePlayers.end(); ++it)
|
|
delete it->second;
|
|
|
|
m_onlinePlayers.clear();
|
|
}
|
|
|
|
void ServerSideNetworkHandler::levelGenerated(Level* level)
|
|
{
|
|
m_pLevel = level;
|
|
|
|
if (m_pMinecraft->m_pLocalPlayer)
|
|
{
|
|
m_pMinecraft->m_pLocalPlayer->m_guid = m_pRakNetPeer->GetMyGUID();
|
|
}
|
|
|
|
level->addListener(this);
|
|
|
|
allowIncomingConnections(m_pMinecraft->getOptions()->m_bServerVisibleDefault);
|
|
|
|
m_onlinePlayers[m_pMinecraft->m_pLocalPlayer->m_guid] = new OnlinePlayer(m_pMinecraft->m_pLocalPlayer, m_pMinecraft->m_pLocalPlayer->m_guid);
|
|
}
|
|
|
|
void ServerSideNetworkHandler::onNewClient(const RakNet::RakNetGUID& guid)
|
|
{
|
|
printf_ignorable("onNewClient, client guid: %s", guid.ToString());
|
|
}
|
|
|
|
void ServerSideNetworkHandler::onDisconnect(const RakNet::RakNetGUID& guid)
|
|
{
|
|
puts_ignorable("onDisconnect");
|
|
|
|
OnlinePlayer* pOnlinePlayer = getPlayerByGUID(guid);
|
|
|
|
if (!pOnlinePlayer)
|
|
return;
|
|
|
|
Player* pPlayer = pOnlinePlayer->m_pPlayer;
|
|
|
|
// erase it from the map
|
|
m_onlinePlayers.erase(m_onlinePlayers.find(guid)); // it better be in our map
|
|
|
|
// tell everyone that they left the game
|
|
displayGameMessage(pPlayer->m_name + " disconnected from the game");
|
|
m_pRakNetInstance->send(new RemoveEntityPacket(pPlayer->m_EntityID));
|
|
|
|
// remove it from our world
|
|
m_pLevel->removeEntity(pPlayer);
|
|
|
|
// delete the online player's entry.
|
|
delete pOnlinePlayer;
|
|
}
|
|
|
|
void ServerSideNetworkHandler::handle(const RakNet::RakNetGUID& guid, LoginPacket* packet)
|
|
{
|
|
if (!m_bAllowIncoming)
|
|
return;
|
|
|
|
puts_ignorable("LoginPacket");
|
|
|
|
// if they're already online, fail
|
|
if (getPlayerByGUID(guid))
|
|
{
|
|
LOG_E("That player is already in the world!");
|
|
return;
|
|
}
|
|
|
|
Player* pPlayer = new Player(m_pLevel);
|
|
pPlayer->m_guid = guid;
|
|
pPlayer->m_name = std::string(packet->m_str.C_String());
|
|
|
|
m_onlinePlayers[guid] = new OnlinePlayer(pPlayer, guid);
|
|
|
|
StartGamePacket sgp;
|
|
sgp.field_4 = m_pLevel->getSeed();
|
|
sgp.field_8 = m_pLevel->getLevelData()->field_20;
|
|
sgp.field_C = pPlayer->m_EntityID;
|
|
sgp.field_10 = pPlayer->m_pos.x;
|
|
sgp.field_14 = pPlayer->m_pos.y - pPlayer->field_84;
|
|
sgp.field_18 = pPlayer->m_pos.z;
|
|
sgp.m_version = 2;
|
|
sgp.m_time = m_pLevel->getTime();
|
|
|
|
RakNet::BitStream sgpbs;
|
|
sgp.write(&sgpbs);
|
|
m_pRakNetPeer->Send(&sgpbs, HIGH_PRIORITY, RELIABLE_ORDERED, 0, guid, false);
|
|
|
|
// send the connecting player info about all other players in the world
|
|
for (int i = 0; i < int(m_pLevel->m_players.size()); i++)
|
|
{
|
|
Player* player = m_pLevel->m_players[i];
|
|
AddPlayerPacket app(player->m_guid, RakNet::RakString(player->m_name.c_str()), player->m_EntityID, player->m_pos.x, player->m_pos.y - player->field_84, player->m_pos.z);
|
|
RakNet::BitStream appbs;
|
|
app.write(&appbs);
|
|
m_pRakNetPeer->Send(&appbs, HIGH_PRIORITY, RELIABLE_ORDERED, 0, guid, false);
|
|
}
|
|
|
|
m_pLevel->addEntity(pPlayer);
|
|
|
|
if (m_pMinecraft->m_pGameMode->isCreativeType())
|
|
pPlayer->m_pInventory->prepareCreativeInventory();
|
|
else
|
|
pPlayer->m_pInventory->prepareSurvivalInventory();
|
|
|
|
m_pMinecraft->m_gui.addMessage(pPlayer->m_name + " joined the game");
|
|
|
|
AddPlayerPacket app(guid, RakNet::RakString(pPlayer->m_name.c_str()), pPlayer->m_EntityID, pPlayer->m_pos.x, pPlayer->m_pos.y - pPlayer->field_84, pPlayer->m_pos.z);
|
|
RakNet::BitStream appbs;
|
|
app.write(&appbs);
|
|
m_pRakNetPeer->Send(&appbs, HIGH_PRIORITY, RELIABLE_ORDERED, 0, guid, true);
|
|
}
|
|
|
|
void ServerSideNetworkHandler::handle(const RakNet::RakNetGUID& guid, MessagePacket* packet)
|
|
{
|
|
OnlinePlayer* pOP = getPlayerByGUID(guid);
|
|
if (!pOP)
|
|
{
|
|
LOG_W("MessagePacket: That jerk %s doesn't actually exist", guid.ToString());
|
|
return;
|
|
}
|
|
|
|
// don't let players send empty messages
|
|
std::string msg(packet->m_str.C_String());
|
|
if (msg.empty())
|
|
return;
|
|
|
|
if (msg[0] == '/')
|
|
{
|
|
LOG_I("CMD: %s: %s", pOP->m_pPlayer->m_name.c_str(), msg.c_str());
|
|
|
|
std::stringstream ss(msg);
|
|
ss.get(); // skip the /
|
|
std::vector<std::string> parms;
|
|
std::string currparm, cmdname;
|
|
// get cmd name
|
|
std::getline(ss, cmdname, ' ');
|
|
// get cmd parms
|
|
while (std::getline(ss, currparm, ' '))
|
|
parms.push_back(currparm);
|
|
|
|
CommandFunction func;
|
|
CommandMap::iterator iter = m_commands.find(cmdname);
|
|
if (iter == m_commands.end())
|
|
{
|
|
sendMessage(pOP, "Unknown command. Type /? for a list of commands.");
|
|
return;
|
|
}
|
|
|
|
func = iter->second;
|
|
|
|
(this->*func)(pOP, parms);
|
|
|
|
return;
|
|
}
|
|
|
|
LOG_I("MSG: <%s> %s", pOP->m_pPlayer->m_name.c_str(), msg.c_str());
|
|
|
|
// send everyone the message
|
|
std::string gameMessage = "<" + pOP->m_pPlayer->m_name + "> " + msg;
|
|
displayGameMessage(gameMessage);
|
|
}
|
|
|
|
void ServerSideNetworkHandler::handle(const RakNet::RakNetGUID& guid, MovePlayerPacket* packet)
|
|
{
|
|
//not in the original
|
|
puts_ignorable("MovePlayerPacket");
|
|
|
|
Entity* pEntity = m_pLevel->getEntity(packet->m_id);
|
|
if (pEntity)
|
|
pEntity->lerpTo(packet->m_x, packet->m_y, packet->m_z, packet->m_yaw, packet->m_pitch, 3);
|
|
|
|
redistributePacket(packet, guid);
|
|
}
|
|
|
|
void ServerSideNetworkHandler::handle(const RakNet::RakNetGUID& guid, PlaceBlockPacket* packet)
|
|
{
|
|
Mob* pMob = (Mob*)m_pLevel->getEntity(packet->m_playerID);
|
|
if (!pMob)
|
|
return;
|
|
|
|
TileID tile = packet->m_tile;
|
|
int face = packet->m_face;
|
|
int x = packet->m_x;
|
|
int y = packet->m_y;
|
|
int z = packet->m_z;
|
|
|
|
printf_ignorable("PlaceBlockPacket: %d", tile);
|
|
|
|
if (!m_pLevel->mayPlace(tile, x, y, z, true))
|
|
return;
|
|
|
|
if (m_pLevel->setTile(x, y, z, tile))
|
|
{
|
|
Tile::tiles[tile]->setPlacedOnFace(m_pLevel, x, y, z, face);
|
|
Tile::tiles[tile]->setPlacedBy(m_pLevel, x, y, z, pMob);
|
|
|
|
const Tile::SoundType* pSound = Tile::tiles[tile]->m_pSound;
|
|
m_pLevel->playSound(float(x) + 0.5f, float(y) + 0.5f, float(z) + 0.5f, "step." + pSound->m_name, 0.5f * (pSound->field_18 + 1.0f), pSound->field_1C * 0.8f);
|
|
}
|
|
|
|
redistributePacket(packet, guid);
|
|
}
|
|
|
|
void ServerSideNetworkHandler::handle(const RakNet::RakNetGUID& guid, RemoveBlockPacket* packet)
|
|
{
|
|
puts_ignorable("RemoveBlockPacket");
|
|
|
|
Player* pPlayer = (Player*)m_pLevel->getEntity(packet->m_playerID);
|
|
if (!pPlayer)
|
|
return;
|
|
|
|
int x = packet->m_x;
|
|
int y = packet->m_y;
|
|
int z = packet->m_z;
|
|
|
|
Tile* pTile = Tile::tiles[m_pLevel->getTile(x, y, z)];
|
|
//int data = m_pLevel->getData(x, y, z);
|
|
bool setTileResult = m_pLevel->setTile(x, y, z, TILE_AIR);
|
|
if (pTile && setTileResult)
|
|
{
|
|
const Tile::SoundType* pSound = pTile->m_pSound;
|
|
m_pMinecraft->m_pSoundEngine->play("step." + pSound->m_name, float(x) + 0.5f, float(y) + 0.5f, float(z) + 0.5f, 0.5f * (pSound->field_18 + 1.0f), pSound->field_1C * 0.8f);
|
|
|
|
// redistribute the packet only if needed
|
|
redistributePacket(packet, guid);
|
|
}
|
|
}
|
|
|
|
void ServerSideNetworkHandler::handle(const RakNet::RakNetGUID& guid, PlayerEquipmentPacket* packet)
|
|
{
|
|
Player* pPlayer = (Player*)m_pLevel->getEntity(packet->m_playerID);
|
|
if (!pPlayer)
|
|
{
|
|
LOG_W("No player with id %d", packet->m_playerID);
|
|
return;
|
|
}
|
|
|
|
#ifndef ORIGINAL_CODE
|
|
if (!Item::items[packet->m_itemID])
|
|
{
|
|
LOG_W("That item %d doesn't actually exist!", packet->m_itemID);
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
if (pPlayer->m_guid == m_pRakNetPeer->GetMyGUID())
|
|
{
|
|
LOG_W("Attempted to modify local player's inventory");
|
|
return;
|
|
}
|
|
|
|
pPlayer->m_pInventory->selectItemById(packet->m_itemID, C_MAX_HOTBAR_ITEMS);
|
|
|
|
redistributePacket(packet, guid);
|
|
}
|
|
|
|
void ServerSideNetworkHandler::handle(const RakNet::RakNetGUID& guid, RequestChunkPacket* packet)
|
|
{
|
|
puts_ignorable("RequestChunkPacket");
|
|
|
|
if (packet->m_x == -9999)
|
|
{
|
|
m_pRakNetInstance->send(guid, new LevelDataPacket(m_pLevel));
|
|
return;
|
|
}
|
|
|
|
LevelChunk* pChunk = m_pLevel->getChunk(packet->m_x, packet->m_z);
|
|
if (!pChunk)
|
|
{
|
|
LOG_E("No chunk at %d, %d", packet->m_x, packet->m_z);
|
|
return;
|
|
}
|
|
|
|
// @NOTE: this allows the client to request empty chunks. Is that okay?
|
|
ChunkDataPacket cdp(pChunk->m_chunkX, pChunk->m_chunkZ, pChunk);
|
|
|
|
RakNet::BitStream bs;
|
|
cdp.write(&bs);
|
|
|
|
m_pRakNetPeer->Send(&bs, HIGH_PRIORITY, RELIABLE_ORDERED, 0, guid, false);
|
|
}
|
|
|
|
void ServerSideNetworkHandler::tileBrightnessChanged(int x, int y, int z)
|
|
{
|
|
}
|
|
|
|
void ServerSideNetworkHandler::tileChanged(int x, int y, int z)
|
|
{
|
|
UpdateBlockPacket ubp;
|
|
|
|
int tile = m_pLevel->getTile(x, y, z);
|
|
int data = m_pLevel->getData(x, y, z);
|
|
|
|
ubp.m_x = x;
|
|
ubp.m_y = y;
|
|
ubp.m_z = z;
|
|
ubp.m_tile = uint8_t(tile);
|
|
ubp.m_data = uint8_t(data);
|
|
|
|
RakNet::BitStream bs;
|
|
ubp.write(&bs);
|
|
|
|
m_pRakNetPeer->Send(&bs, HIGH_PRIORITY, RELIABLE_ORDERED, 0, RakNet::AddressOrGUID(), true);
|
|
}
|
|
|
|
void ServerSideNetworkHandler::allowIncomingConnections(bool b)
|
|
{
|
|
if (b)
|
|
{
|
|
m_pRakNetInstance->announceServer(m_pMinecraft->getOptions()->m_playerName);
|
|
}
|
|
else
|
|
{
|
|
m_pRakNetInstance->announceServer("");
|
|
}
|
|
|
|
m_bAllowIncoming = b;
|
|
}
|
|
|
|
void ServerSideNetworkHandler::displayGameMessage(const std::string& msg)
|
|
{
|
|
m_pMinecraft->m_gui.addMessage(msg);
|
|
m_pRakNetInstance->send(new MessagePacket(msg));
|
|
}
|
|
|
|
void ServerSideNetworkHandler::sendMessage(const RakNet::RakNetGUID& guid, const std::string& msg)
|
|
{
|
|
if (m_pRakNetPeer->GetMyGUID() == guid)
|
|
{
|
|
m_pMinecraft->m_gui.addMessage(msg);
|
|
return;
|
|
}
|
|
|
|
m_pRakNetInstance->send(guid, new MessagePacket(msg));
|
|
}
|
|
|
|
void ServerSideNetworkHandler::sendMessage(OnlinePlayer* player, const std::string& msg)
|
|
{
|
|
sendMessage(player->m_guid, msg);
|
|
}
|
|
|
|
void ServerSideNetworkHandler::redistributePacket(Packet* packet, const RakNet::RakNetGUID& source)
|
|
{
|
|
RakNet::BitStream bs;
|
|
packet->write(&bs);
|
|
|
|
m_pRakNetPeer->Send(&bs, HIGH_PRIORITY, RELIABLE_ORDERED, 0, source, true);
|
|
}
|
|
|
|
OnlinePlayer* ServerSideNetworkHandler::getPlayerByGUID(const RakNet::RakNetGUID& guid)
|
|
{
|
|
OnlinePlayerMap::iterator iter = m_onlinePlayers.find(guid);
|
|
if (iter == m_onlinePlayers.end())
|
|
return nullptr;
|
|
|
|
return iter->second;
|
|
}
|
|
|
|
///////////////// In-Game Commands /////////////////
|
|
|
|
void ServerSideNetworkHandler::setupCommands()
|
|
{
|
|
m_commands["?"] = &ServerSideNetworkHandler::commandHelp;
|
|
m_commands["help"] = &ServerSideNetworkHandler::commandHelp;
|
|
m_commands["stats"] = &ServerSideNetworkHandler::commandStats;
|
|
m_commands["time"] = &ServerSideNetworkHandler::commandTime;
|
|
m_commands["seed"] = &ServerSideNetworkHandler::commandSeed;
|
|
m_commands["tp"] = &ServerSideNetworkHandler::commandTP;
|
|
}
|
|
|
|
void ServerSideNetworkHandler::commandHelp(OnlinePlayer* player, const std::vector<std::string>& parms)
|
|
{
|
|
std::stringstream ss;
|
|
ss << ">> Available commands:";
|
|
|
|
for (CommandMap::iterator it = m_commands.begin(); it != m_commands.end(); ++it)
|
|
{
|
|
ss << " /" << it->first;
|
|
}
|
|
|
|
sendMessage(player, ss.str());
|
|
}
|
|
|
|
void ServerSideNetworkHandler::commandStats(OnlinePlayer* player, const std::vector<std::string>& parms)
|
|
{
|
|
if (!m_pLevel)
|
|
return;
|
|
|
|
std::stringstream ss;
|
|
ss << "Server uptime: " << getTimeS() << " seconds.\n";
|
|
ss << "Host's name: " << m_pMinecraft->m_pUser->field_0 << "\n";
|
|
|
|
int nPlayers = int(m_onlinePlayers.size());
|
|
if (nPlayers == 1)
|
|
ss << "There is 1 player online.";
|
|
else
|
|
ss << "There are " << nPlayers << " players online.";
|
|
|
|
sendMessage(player, ss.str());
|
|
}
|
|
|
|
void ServerSideNetworkHandler::commandTime(OnlinePlayer* player, const std::vector<std::string>& parms)
|
|
{
|
|
if (!m_pLevel)
|
|
return;
|
|
|
|
if (!parms.empty())
|
|
{
|
|
int t = 0;
|
|
if (!sscanf(parms[0].c_str(), "%d", &t))
|
|
{
|
|
sendMessage(player, "Usage: /time [new time value]");
|
|
return;
|
|
}
|
|
|
|
m_pLevel->setTime(t);
|
|
sendMessage(player, "Time has been set to " + parms[0]);
|
|
|
|
return;
|
|
}
|
|
|
|
std::stringstream ss;
|
|
ss << "In-game time: ";
|
|
ss << m_pLevel->getTime();
|
|
ss << ". Day " << m_pLevel->getTime() / 24000;
|
|
sendMessage(player, ss.str());
|
|
}
|
|
|
|
void ServerSideNetworkHandler::commandSeed(OnlinePlayer* player, const std::vector<std::string>& parms)
|
|
{
|
|
if (!m_pLevel)
|
|
return;
|
|
|
|
std::stringstream ss;
|
|
ss << "World generation seed: ";
|
|
ss << m_pLevel->getSeed();
|
|
sendMessage(player, ss.str());
|
|
}
|
|
|
|
void ServerSideNetworkHandler::commandTP(OnlinePlayer* player, const std::vector<std::string>& parms)
|
|
{
|
|
if (!m_pLevel)
|
|
return;
|
|
|
|
if (parms.size() != 3)
|
|
{
|
|
sendMessage(player, "Usage: /tp <x> <y> <z>");
|
|
return;
|
|
}
|
|
|
|
if (player->m_pPlayer != this->m_pMinecraft->m_pLocalPlayer)
|
|
{
|
|
sendMessage(player, "Sorry, only the host can use this command at the moment");
|
|
return;
|
|
}
|
|
|
|
Vec3 pos = player->m_pPlayer->getPos(1.0f);
|
|
|
|
float x = pos.x, y = pos.y, z = pos.z;
|
|
|
|
std::stringstream ss;
|
|
if (parms[0] != "~")
|
|
{
|
|
ss = std::stringstream(parms[0]);
|
|
ss >> x;
|
|
}
|
|
if (parms[1] != "~")
|
|
{
|
|
ss = std::stringstream(parms[1]);
|
|
ss >> y;
|
|
}
|
|
if (parms[2] != "~")
|
|
{
|
|
ss = std::stringstream(parms[2]);
|
|
ss >> z;
|
|
}
|
|
|
|
ss = std::stringstream();
|
|
ss << "Teleported to " << x << ", " << y << ", " << z;
|
|
|
|
player->m_pPlayer->setPos(x, y, z);
|
|
|
|
sendMessage(player, ss.str());
|
|
}
|