#include "stdafx.h" #include "PlayerConnection.h" #include "ServerPlayer.h" #include "ServerLevel.h" #include "ServerPlayerGameMode.h" #include "PlayerList.h" #include "MinecraftServer.h" #include "..\Minecraft.World\net.minecraft.commands.h" #include "..\Minecraft.World\net.minecraft.network.h" #include "..\Minecraft.World\net.minecraft.world.entity.item.h" #include "..\Minecraft.World\net.minecraft.world.level.h" #include "..\Minecraft.World\net.minecraft.world.level.dimension.h" #include "..\Minecraft.World\net.minecraft.world.item.h" #include "..\Minecraft.World\net.minecraft.world.item.trading.h" #include "..\Minecraft.World\net.minecraft.world.inventory.h" #include "..\Minecraft.World\net.minecraft.world.level.tile.entity.h" #include "..\Minecraft.World\net.minecraft.world.level.saveddata.h" #include "..\Minecraft.World\net.minecraft.network.h" #include "..\Minecraft.World\net.minecraft.world.food.h" #include "..\Minecraft.World\AABB.h" #include "..\Minecraft.World\Pos.h" #include "..\Minecraft.World\SharedConstants.h" #include "..\Minecraft.World\Socket.h" #include "..\Minecraft.World\Achievements.h" #include "..\Minecraft.World\net.minecraft.h" #include "EntityTracker.h" #include "ServerConnection.h" #include "..\Minecraft.World\GenericStats.h" #include "..\Minecraft.World\JavaMath.h" #include #include #include #include #include #include #include #include "..\Minecraft.World\InputOutputStream.h" #ifdef _WINDOWS64 #include "Windows64\Network\WinsockNetLayer.h" #endif // 4J Added #include "..\Minecraft.World\net.minecraft.world.item.crafting.h" #include "Options.h" Random PlayerConnection::random; namespace { const wstring LCE_TRANSFER_PACKET = L"LCE|Xfer"; const unsigned int MINIGAME_SETTINGS_FLAG = 0x80000000u; const unsigned int MINIGAME_SETTINGS_TYPE_MASK = 0x70000000u; const unsigned int MINIGAME_SETTINGS_TYPE_SHIFT = 28u; const unsigned int MINIGAME_SETTINGS_ROLE_MASK = 0x0C000000u; const unsigned int MINIGAME_SETTINGS_ROLE_SHIFT = 26u; const unsigned int MINIGAME_SETTINGS_QUEUE_MASK = 0x03000000u; const unsigned int MINIGAME_SETTINGS_QUEUE_SHIFT = 24u; const unsigned int MINIGAME_TYPE_BEDWARS = 3u; const unsigned int MINIGAME_ROLE_HUB = 0u; const unsigned int MINIGAME_ROLE_MATCH = 1u; const int PARTY_INVITE_TICKS = SharedConstants::TICKS_PER_SECOND * 30; struct BedwarsQueueDef { const wchar_t *name; int npcOffX; int npcOffZ; int queueOffX; int queueOffZ; float queueYaw; int partyLimit; int requiredPlayers; }; static const BedwarsQueueDef BEDWARS_QUEUE_DEFS[] = { { L"Solo", -3, 0, -8, 0, 90.0f, 1, 2 }, { L"Doubles", 0, 3, 0, 8, 180.0f, 2, 4 }, { L"Squads", 3, 0, 8, 0, 270.0f, 4, 8 }, { L"Practice", 0, -3, 0, -8, 0.0f, 1, 1 }, }; struct PartyInvite { BYTE leaderSmallId; int expireTick; }; struct BedwarsQueueEntry { BYTE leaderSmallId; vector members; int queuedTick; }; static map s_partyLeaderByMember; static map > s_partyMembersByLeader; static map s_partyInvites; static vector s_bedwarsQueueEntries[4]; static map s_queueIndexByMember; static int s_lastQueueProcessTick = -1; struct ProxyRoute { string hostIp; int hostPort; wstring displayName; }; static map s_proxyRoutes; static bool s_proxyRoutesLoaded = false; wstring ToLowerText(const wstring &value) { wstring result = value; for (size_t i = 0; i < result.length(); ++i) { result[i] = (wchar_t)std::towlower(result[i]); } return result; } vector SplitCommandTokens(const wstring &message) { vector tokens; size_t pos = 0; while (pos < message.length()) { while (pos < message.length() && std::iswspace(message[pos])) { ++pos; } if (pos >= message.length()) { break; } size_t end = pos; while (end < message.length() && !std::iswspace(message[end])) { ++end; } tokens.push_back(message.substr(pos, end - pos)); pos = end; } return tokens; } string TrimAsciiString(const string &value) { size_t first = 0; while (first < value.length() && std::isspace((unsigned char)value[first])) { ++first; } size_t last = value.length(); while (last > first && std::isspace((unsigned char)value[last - 1])) { --last; } return value.substr(first, last - first); } wstring AsciiToWide(const string &value) { wstring out; out.reserve(value.length()); for (size_t i = 0; i < value.length(); ++i) { out.push_back((wchar_t)(unsigned char)value[i]); } return out; } bool OpenProxyRoutesFile(std::ifstream &in, string &outPath) { vector candidates; candidates.push_back("proxy-worlds.properties"); candidates.push_back("..\\proxy-worlds.properties"); candidates.push_back("..\\..\\proxy-worlds.properties"); candidates.push_back("..\\..\\..\\proxy-worlds.properties"); char modulePath[MAX_PATH] = {0}; if (::GetModuleFileNameA(NULL, modulePath, MAX_PATH) > 0) { string exePath(modulePath); size_t slash = exePath.find_last_of("\\/"); if (slash != string::npos) { string exeDir = exePath.substr(0, slash); candidates.push_back(exeDir + "\\proxy-worlds.properties"); candidates.push_back(exeDir + "\\..\\proxy-worlds.properties"); candidates.push_back(exeDir + "\\..\\..\\proxy-worlds.properties"); } } for (size_t i = 0; i < candidates.size(); ++i) { in.clear(); in.open(candidates[i].c_str(), std::ios::in); if (in.good()) { outPath = candidates[i]; return true; } } return false; } void LoadProxyRoutes(bool forceReload) { if (s_proxyRoutesLoaded && !forceReload) { return; } s_proxyRoutesLoaded = true; s_proxyRoutes.clear(); std::ifstream in; string loadedFromPath; if (!OpenProxyRoutesFile(in, loadedFromPath)) { return; } string line; while (std::getline(in, line)) { const size_t commentPos = line.find('#'); if (commentPos != string::npos) { line = line.substr(0, commentPos); } line = TrimAsciiString(line); if (line.empty()) { continue; } const size_t equalsPos = line.find('='); if (equalsPos == string::npos) { continue; } string routeNameRaw = TrimAsciiString(line.substr(0, equalsPos)); string routeSpecRaw = TrimAsciiString(line.substr(equalsPos + 1)); if (routeNameRaw.empty() || routeSpecRaw.empty()) { continue; } string displayNameRaw = routeNameRaw; const size_t pipePos = routeSpecRaw.find('|'); if (pipePos != string::npos) { displayNameRaw = TrimAsciiString(routeSpecRaw.substr(pipePos + 1)); routeSpecRaw = TrimAsciiString(routeSpecRaw.substr(0, pipePos)); } const size_t colonPos = routeSpecRaw.rfind(':'); if (colonPos == string::npos) { continue; } string ip = TrimAsciiString(routeSpecRaw.substr(0, colonPos)); string portText = TrimAsciiString(routeSpecRaw.substr(colonPos + 1)); if (ip.empty() || portText.empty()) { continue; } char *portEnd = NULL; long parsedPort = std::strtol(portText.c_str(), &portEnd, 10); if (portEnd == NULL || *portEnd != '\0' || parsedPort <= 0 || parsedPort > 65535) { continue; } ProxyRoute route; route.hostIp = ip; route.hostPort = (int)parsedPort; route.displayName = AsciiToWide(displayNameRaw); if (route.displayName.empty()) { route.displayName = AsciiToWide(routeNameRaw); } s_proxyRoutes[ToLowerText(AsciiToWide(routeNameRaw))] = route; } } bool TryGetProxyRoute(const wstring &routeName, ProxyRoute &outRoute) { LoadProxyRoutes(false); auto it = s_proxyRoutes.find(ToLowerText(routeName)); if (it == s_proxyRoutes.end()) { return false; } outRoute = it->second; return true; } int GetQueueIndexFromName(const wstring &name) { const wstring lower = ToLowerText(name); if (lower == L"solo") return 0; if (lower == L"double" || lower == L"doubles") return 1; if (lower == L"squad" || lower == L"squads") return 2; if (lower == L"practice") return 3; return -1; } wstring GetQueueDisplayName(int queueIndex) { if (queueIndex < 0 || queueIndex >= (int)(sizeof(BEDWARS_QUEUE_DEFS) / sizeof(BEDWARS_QUEUE_DEFS[0]))) { return L"Unknown"; } return BEDWARS_QUEUE_DEFS[queueIndex].name; } BYTE GetSmallIdForPlayer(shared_ptr serverPlayer) { if (serverPlayer == NULL || serverPlayer->connection == NULL) { return 0xFF; } INetworkPlayer *networkPlayer = serverPlayer->connection->getNetworkPlayer(); if (networkPlayer == NULL) { return 0xFF; } return networkPlayer->GetSmallId(); } shared_ptr FindPlayerBySmallId(MinecraftServer *server, BYTE smallId) { if (server == NULL || server->getPlayers() == NULL) { return nullptr; } for (AUTO_VAR(it, server->getPlayers()->players.begin()); it != server->getPlayers()->players.end(); ++it) { shared_ptr check = *it; if (GetSmallIdForPlayer(check) == smallId) { return check; } } return nullptr; } shared_ptr FindPlayerByNameInsensitive(MinecraftServer *server, const wstring &name) { if (server == NULL || server->getPlayers() == NULL) { return nullptr; } const wstring wanted = ToLowerText(name); for (AUTO_VAR(it, server->getPlayers()->players.begin()); it != server->getPlayers()->players.end(); ++it) { shared_ptr check = *it; if (ToLowerText(check->name) == wanted) { return check; } } return nullptr; } wstring GetPlayerNameBySmallId(MinecraftServer *server, BYTE smallId) { shared_ptr target = FindPlayerBySmallId(server, smallId); if (target != NULL) { return target->name; } wchar_t fallback[32]; swprintf_s(fallback, L"Player%u", (unsigned int)smallId); return fallback; } void SendPlayerMessage(shared_ptr target, const wstring &message) { if (target != NULL) { target->sendMessage(message, ChatPacket::e_ChatCustom); } } bool IsBedwarsHostSettings(unsigned int hostSettings) { if ((hostSettings & MINIGAME_SETTINGS_FLAG) == 0) { return false; } return (((hostSettings & MINIGAME_SETTINGS_TYPE_MASK) >> MINIGAME_SETTINGS_TYPE_SHIFT) == MINIGAME_TYPE_BEDWARS); } unsigned int GetBedwarsRoleFromSettings(unsigned int hostSettings) { return (hostSettings & MINIGAME_SETTINGS_ROLE_MASK) >> MINIGAME_SETTINGS_ROLE_SHIFT; } unsigned int GetBedwarsQueueModeFromSettings(unsigned int hostSettings) { return (hostSettings & MINIGAME_SETTINGS_QUEUE_MASK) >> MINIGAME_SETTINGS_QUEUE_SHIFT; } bool IsBedwarsHubSettings(unsigned int hostSettings) { if (!IsBedwarsHostSettings(hostSettings)) { return false; } return GetBedwarsRoleFromSettings(hostSettings) == MINIGAME_ROLE_HUB; } unsigned int ApplyBedwarsSessionMetadata(unsigned int hostSettings, unsigned int role, unsigned int queueMode) { hostSettings |= MINIGAME_SETTINGS_FLAG; hostSettings &= ~MINIGAME_SETTINGS_TYPE_MASK; hostSettings |= (MINIGAME_TYPE_BEDWARS << MINIGAME_SETTINGS_TYPE_SHIFT); hostSettings &= ~MINIGAME_SETTINGS_ROLE_MASK; hostSettings |= ((role & 0x3u) << MINIGAME_SETTINGS_ROLE_SHIFT); hostSettings &= ~MINIGAME_SETTINGS_QUEUE_MASK; hostSettings |= ((queueMode & 0x3u) << MINIGAME_SETTINGS_QUEUE_SHIFT); return hostSettings; } BYTE GetPartyLeaderForMember(BYTE memberSmallId) { auto it = s_partyLeaderByMember.find(memberSmallId); if (it == s_partyLeaderByMember.end()) { return memberSmallId; } return it->second; } void EnsurePartyLeaderEntry(BYTE leaderSmallId) { if (s_partyMembersByLeader.find(leaderSmallId) == s_partyMembersByLeader.end()) { s_partyMembersByLeader[leaderSmallId] = set(); } s_partyMembersByLeader[leaderSmallId].insert(leaderSmallId); s_partyLeaderByMember[leaderSmallId] = leaderSmallId; } vector GetPartyMembersForPlayer(BYTE memberSmallId) { vector members; const BYTE leader = GetPartyLeaderForMember(memberSmallId); auto it = s_partyMembersByLeader.find(leader); if (it == s_partyMembersByLeader.end()) { members.push_back(memberSmallId); return members; } for (AUTO_VAR(itM, it->second.begin()); itM != it->second.end(); ++itM) { members.push_back(*itM); } if (members.empty()) { members.push_back(memberSmallId); } return members; } void RemoveQueueGroupContainingMember(MinecraftServer *server, BYTE memberSmallId, bool announce, const wchar_t *reason) { auto queueIt = s_queueIndexByMember.find(memberSmallId); if (queueIt == s_queueIndexByMember.end()) { return; } const int queueIndex = queueIt->second; if (queueIndex < 0 || queueIndex >= (int)(sizeof(BEDWARS_QUEUE_DEFS) / sizeof(BEDWARS_QUEUE_DEFS[0]))) { s_queueIndexByMember.erase(memberSmallId); return; } vector &entries = s_bedwarsQueueEntries[queueIndex]; for (size_t i = 0; i < entries.size(); ++i) { bool contains = false; for (size_t m = 0; m < entries[i].members.size(); ++m) { if (entries[i].members[m] == memberSmallId) { contains = true; break; } } if (!contains) { continue; } for (size_t m = 0; m < entries[i].members.size(); ++m) { const BYTE removeId = entries[i].members[m]; s_queueIndexByMember.erase(removeId); if (announce) { shared_ptr target = FindPlayerBySmallId(server, removeId); if (target != NULL) { wstring msg = L"Left Bedwars "; msg += GetQueueDisplayName(queueIndex); msg += L" queue"; if (reason != NULL && reason[0] != 0) { msg += L" ("; msg += reason; msg += L")"; } msg += L"."; SendPlayerMessage(target, msg); } } } entries.erase(entries.begin() + i); return; } // Defensive cleanup: stale map entry with no corresponding queue group. s_queueIndexByMember.erase(memberSmallId); } void RemovePlayerFromAllQueues(MinecraftServer *server, BYTE memberSmallId, bool announce, const wchar_t *reason) { if (memberSmallId == 0xFF) { return; } while (s_queueIndexByMember.find(memberSmallId) != s_queueIndexByMember.end()) { RemoveQueueGroupContainingMember(server, memberSmallId, announce, reason); } } void LeaveParty(MinecraftServer *server, BYTE memberSmallId, bool announce) { if (memberSmallId == 0xFF) { return; } const BYTE leaderSmallId = GetPartyLeaderForMember(memberSmallId); auto partyIt = s_partyMembersByLeader.find(leaderSmallId); if (partyIt == s_partyMembersByLeader.end()) { s_partyLeaderByMember.erase(memberSmallId); s_partyInvites.erase(memberSmallId); return; } if (leaderSmallId == memberSmallId) { for (AUTO_VAR(it, partyIt->second.begin()); it != partyIt->second.end(); ++it) { const BYTE other = *it; s_partyLeaderByMember.erase(other); if (announce) { shared_ptr target = FindPlayerBySmallId(server, other); if (target != NULL) { SendPlayerMessage(target, L"Party disbanded."); } } } s_partyMembersByLeader.erase(leaderSmallId); } else { partyIt->second.erase(memberSmallId); s_partyLeaderByMember.erase(memberSmallId); if (announce) { shared_ptr leader = FindPlayerBySmallId(server, leaderSmallId); if (leader != NULL) { wstring leftMsg = GetPlayerNameBySmallId(server, memberSmallId); leftMsg += L" left your party."; SendPlayerMessage(leader, leftMsg); } shared_ptr member = FindPlayerBySmallId(server, memberSmallId); if (member != NULL) { SendPlayerMessage(member, L"You left the party."); } } if (partyIt->second.size() <= 1) { s_partyMembersByLeader.erase(leaderSmallId); s_partyLeaderByMember.erase(leaderSmallId); } } s_partyInvites.erase(memberSmallId); for (AUTO_VAR(itI, s_partyInvites.begin()); itI != s_partyInvites.end();) { if (itI->second.leaderSmallId == memberSmallId) { itI = s_partyInvites.erase(itI); } else { ++itI; } } } void ClearPlayerStateOnDisconnect(MinecraftServer *server, shared_ptr leavingPlayer) { const BYTE smallId = GetSmallIdForPlayer(leavingPlayer); if (smallId == 0xFF) { return; } RemovePlayerFromAllQueues(server, smallId, false, NULL); LeaveParty(server, smallId, false); s_partyInvites.erase(smallId); } vector BuildQueueGroupMembers(MinecraftServer *server, BYTE requestorSmallId) { vector members; vector partyMembers = GetPartyMembersForPlayer(requestorSmallId); for (size_t i = 0; i < partyMembers.size(); ++i) { if (FindPlayerBySmallId(server, partyMembers[i]) != NULL) { members.push_back(partyMembers[i]); } } if (members.empty()) { members.push_back(requestorSmallId); } std::sort(members.begin(), members.end()); members.erase(std::unique(members.begin(), members.end()), members.end()); return members; } bool JoinBedwarsQueue(MinecraftServer *server, shared_ptr requestingPlayer, int queueIndex) { if (server == NULL || requestingPlayer == NULL) { return false; } if (queueIndex < 0 || queueIndex >= (int)(sizeof(BEDWARS_QUEUE_DEFS) / sizeof(BEDWARS_QUEUE_DEFS[0]))) { return false; } const BYTE requestorSmallId = GetSmallIdForPlayer(requestingPlayer); if (requestorSmallId == 0xFF) { return false; } vector members = BuildQueueGroupMembers(server, requestorSmallId); const BedwarsQueueDef &queue = BEDWARS_QUEUE_DEFS[queueIndex]; if ((int)members.size() > queue.partyLimit) { wstring msg = L"Your party is too large for "; msg += queue.name; msg += L" queue."; SendPlayerMessage(requestingPlayer, msg); return false; } for (size_t i = 0; i < members.size(); ++i) { RemovePlayerFromAllQueues(server, members[i], false, NULL); } BedwarsQueueEntry entry; entry.leaderSmallId = GetPartyLeaderForMember(requestorSmallId); entry.members = members; entry.queuedTick = server->tickCount; s_bedwarsQueueEntries[queueIndex].push_back(entry); for (size_t i = 0; i < members.size(); ++i) { s_queueIndexByMember[members[i]] = queueIndex; } for (size_t i = 0; i < members.size(); ++i) { shared_ptr target = FindPlayerBySmallId(server, members[i]); if (target != NULL) { wstring queuedMsg = L"Queued for Bedwars "; queuedMsg += queue.name; queuedMsg += L"."; SendPlayerMessage(target, queuedMsg); } } return true; } bool IsBedwarsHubProtectedArea(ServerLevel *level, int x, int y, int z) { if (level == NULL || !IsBedwarsHubSettings(app.GetGameHostOption(eGameHostOption_All))) { return false; } Pos *spawnPos = level->getSharedSpawnPos(); if (spawnPos == NULL) { return false; } const int dx = (int)Mth::abs((float)(x - spawnPos->x)); const int dz = (int)Mth::abs((float)(z - spawnPos->z)); const int minY = spawnPos->y - 3; const int maxY = spawnPos->y + 12; delete spawnPos; return (dx <= 24 && dz <= 24 && y >= minY && y <= maxY); } int FindBedwarsQueueIndexForTarget(ServerLevel *level, shared_ptr target) { if (level == NULL || target == NULL || target->GetType() != eTYPE_VILLAGER) { return -1; } Pos *spawnPos = level->getSharedSpawnPos(); if (spawnPos == NULL) { return -1; } const double spawnX = (double)spawnPos->x + 0.5; const double spawnZ = (double)spawnPos->z + 0.5; delete spawnPos; const double maxMatchDistSqr = 2.5 * 2.5; for (size_t i = 0; i < (sizeof(BEDWARS_QUEUE_DEFS) / sizeof(BEDWARS_QUEUE_DEFS[0])); ++i) { const double npcX = spawnX + (double)BEDWARS_QUEUE_DEFS[i].npcOffX; const double npcZ = spawnZ + (double)BEDWARS_QUEUE_DEFS[i].npcOffZ; const double dx = target->x - npcX; const double dz = target->z - npcZ; if ((dx * dx + dz * dz) <= maxMatchDistSqr) { return (int)i; } } return -1; } #ifdef _WINDOWS64 bool PickQueueEntriesRecursive(const vector &entries, size_t startIndex, int remainingPlayers, vector &pickedEntries) { if (remainingPlayers == 0) { return true; } if (startIndex >= entries.size() || remainingPlayers < 0) { return false; } for (size_t i = startIndex; i < entries.size(); ++i) { const int groupSize = (int)entries[i].members.size(); if (groupSize <= 0 || groupSize > remainingPlayers) { continue; } pickedEntries.push_back(i); if (PickQueueEntriesRecursive(entries, i + 1, remainingPlayers - groupSize, pickedEntries)) { return true; } pickedEntries.pop_back(); } return false; } bool PickQueueEntriesForMatch(const vector &entries, int requiredPlayers, vector &pickedEntries) { pickedEntries.clear(); if (requiredPlayers <= 0) { return false; } return PickQueueEntriesRecursive(entries, 0, requiredPlayers, pickedEntries); } bool IsRemoteMatchServerCandidate(const Win64LANSession &session, int queueIndex, int requiredPlayers) { if (!session.isJoinable) { return false; } if (!IsBedwarsHostSettings(session.gameHostSettings)) { return false; } if (GetBedwarsRoleFromSettings(session.gameHostSettings) != MINIGAME_ROLE_MATCH) { return false; } if ((int)GetBedwarsQueueModeFromSettings(session.gameHostSettings) != queueIndex) { return false; } int maxPlayers = (int)session.maxPlayers; if (maxPlayers <= 0) { maxPlayers = MINECRAFT_NET_MAX_PLAYERS; } const int slotsAvailable = maxPlayers - (int)session.playerCount; if (slotsAvailable < requiredPlayers) { return false; } return true; } bool FindBestQueueTarget(int queueIndex, int requiredPlayers, string &outIp, int &outPort) { std::vector sessions = WinsockNetLayer::GetDiscoveredSessions(); int bestFreeSlots = -1; int bestPlayers = 9999; for (size_t i = 0; i < sessions.size(); ++i) { const Win64LANSession &session = sessions[i]; if (!IsRemoteMatchServerCandidate(session, queueIndex, requiredPlayers)) { continue; } int maxPlayers = (int)session.maxPlayers; if (maxPlayers <= 0) { maxPlayers = MINECRAFT_NET_MAX_PLAYERS; } const int freeSlots = maxPlayers - (int)session.playerCount; if (freeSlots > bestFreeSlots || (freeSlots == bestFreeSlots && (int)session.playerCount < bestPlayers)) { bestFreeSlots = freeSlots; bestPlayers = (int)session.playerCount; outIp = session.hostIP; outPort = session.hostPort; } } return (bestFreeSlots >= 0 && !outIp.empty() && outPort > 0); } void SendTransferPayloadEx(shared_ptr player, const string &hostIp, int hostPort, int queueIndex, const wstring &destinationName) { if (player == NULL || player->connection == NULL) { return; } wstring hostIpW; for (size_t i = 0; i < hostIp.length(); ++i) { hostIpW.push_back((wchar_t)hostIp[i]); } ByteArrayOutputStream rawOutput; DataOutputStream output(&rawOutput); output.writeByte((byte)1); output.writeByte((byte)queueIndex); output.writeInt(hostPort); output.writeUTF(hostIpW); output.writeUTF(destinationName); player->connection->send(shared_ptr(new CustomPayloadPacket(LCE_TRANSFER_PACKET, rawOutput.toByteArray()))); } void SendTransferPayload(shared_ptr player, const string &hostIp, int hostPort, int queueIndex) { SendTransferPayloadEx(player, hostIp, hostPort, queueIndex, GetQueueDisplayName(queueIndex)); } #endif void ProcessBedwarsQueueSystem(MinecraftServer *server) { if (server == NULL) { return; } if (server->getPlayers() == NULL || server->getPlayers()->players.empty()) { for (int i = 0; i < (int)(sizeof(BEDWARS_QUEUE_DEFS) / sizeof(BEDWARS_QUEUE_DEFS[0])); ++i) { s_bedwarsQueueEntries[i].clear(); } s_queueIndexByMember.clear(); s_partyInvites.clear(); s_partyLeaderByMember.clear(); s_partyMembersByLeader.clear(); return; } if (s_lastQueueProcessTick == server->tickCount) { return; } s_lastQueueProcessTick = server->tickCount; for (AUTO_VAR(itInvite, s_partyInvites.begin()); itInvite != s_partyInvites.end();) { if (server->tickCount >= itInvite->second.expireTick) { itInvite = s_partyInvites.erase(itInvite); } else { ++itInvite; } } for (int queueIndex = 0; queueIndex < (int)(sizeof(BEDWARS_QUEUE_DEFS) / sizeof(BEDWARS_QUEUE_DEFS[0])); ++queueIndex) { vector &entries = s_bedwarsQueueEntries[queueIndex]; for (size_t i = 0; i < entries.size();) { bool valid = true; for (size_t m = 0; m < entries[i].members.size(); ++m) { if (FindPlayerBySmallId(server, entries[i].members[m]) == NULL) { valid = false; break; } } if (!valid) { for (size_t m = 0; m < entries[i].members.size(); ++m) { s_queueIndexByMember.erase(entries[i].members[m]); } entries.erase(entries.begin() + i); continue; } ++i; } } #ifdef _WINDOWS64 if (!IsBedwarsHubSettings(app.GetGameHostOption(eGameHostOption_All))) { return; } for (int queueIndex = 0; queueIndex < (int)(sizeof(BEDWARS_QUEUE_DEFS) / sizeof(BEDWARS_QUEUE_DEFS[0])); ++queueIndex) { vector &entries = s_bedwarsQueueEntries[queueIndex]; if (entries.empty()) { continue; } const int requiredPlayers = BEDWARS_QUEUE_DEFS[queueIndex].requiredPlayers; int totalQueued = 0; for (size_t i = 0; i < entries.size(); ++i) { totalQueued += (int)entries[i].members.size(); } if (totalQueued < requiredPlayers) { continue; } string targetIp; int targetPort = 0; if (!FindBestQueueTarget(queueIndex, requiredPlayers, targetIp, targetPort)) { continue; } vector pickedEntries; if (!PickQueueEntriesForMatch(entries, requiredPlayers, pickedEntries)) { continue; } app.DebugPrintf("Bedwars queue %d matched %d players -> %s:%d\n", queueIndex, requiredPlayers, targetIp.c_str(), targetPort); for (int pick = (int)pickedEntries.size() - 1; pick >= 0; --pick) { const size_t entryIndex = pickedEntries[pick]; if (entryIndex >= entries.size()) { continue; } const BedwarsQueueEntry entry = entries[entryIndex]; for (size_t m = 0; m < entry.members.size(); ++m) { const BYTE memberId = entry.members[m]; s_queueIndexByMember.erase(memberId); shared_ptr target = FindPlayerBySmallId(server, memberId); if (target != NULL) { SendPlayerMessage(target, L"Match found. Transferring to game server..."); SendTransferPayload(target, targetIp, targetPort, queueIndex); } } entries.erase(entries.begin() + entryIndex); } } #endif } } PlayerConnection::PlayerConnection(MinecraftServer *server, Connection *connection, shared_ptr player) { // 4J - added initialisers done = false; tickCount = 0; aboveGroundTickCount = 0; xLastOk = yLastOk = zLastOk = 0; synched = true; didTick = false; lastKeepAliveId = 0; lastKeepAliveTime = 0; lastKeepAliveTick = 0; chatSpamTickCount = 0; dropSpamTickCount = 0; this->server = server; this->connection = connection; connection->setListener(this); this->player = player; // player->connection = this; // 4J - moved out as we can't assign in a ctor InitializeCriticalSection(&done_cs); m_bCloseOnTick = false; m_bWasKicked = false; m_friendsOnlyUGC = false; m_offlineXUID = INVALID_XUID; m_onlineXUID = INVALID_XUID; m_bHasClientTickedOnce = false; setShowOnMaps(app.GetGameHostOption(eGameHostOption_Gamertags)!=0?true:false); } PlayerConnection::~PlayerConnection() { delete connection; DeleteCriticalSection(&done_cs); } void PlayerConnection::tick() { if( done ) return; if( m_bCloseOnTick ) { disconnect( DisconnectPacket::eDisconnect_Closed ); return; } didTick = false; tickCount++; connection->tick(); if(done) return; if ((tickCount - lastKeepAliveTick) > 20 * 1) { lastKeepAliveTick = tickCount; lastKeepAliveTime = System::nanoTime() / 1000000; lastKeepAliveId = random.nextInt(); send( shared_ptr( new KeepAlivePacket(lastKeepAliveId) ) ); } // if (!didTick) { // player->doTick(false); // } if (chatSpamTickCount > 0) { chatSpamTickCount--; } if (dropSpamTickCount > 0) { dropSpamTickCount--; } ProcessBedwarsQueueSystem(server); } void PlayerConnection::disconnect(DisconnectPacket::eDisconnectReason reason) { EnterCriticalSection(&done_cs); if( done ) { LeaveCriticalSection(&done_cs); return; } app.DebugPrintf("PlayerConnection disconect reason: %d\n", reason ); ClearPlayerStateOnDisconnect(server, player); player->disconnect(); // 4J Stu - Need to remove the player from the receiving list before their socket is NULLed so that we can find another player on their system server->getPlayers()->removePlayerFromReceiving( player ); send( shared_ptr( new DisconnectPacket(reason) )); connection->sendAndQuit(); // 4J-PB - removed, since it needs to be localised in the language the client is in //server->players->broadcastAll( shared_ptr( new ChatPacket(L"§e" + player->name + L" left the game.") ) ); if(getWasKicked()) { server->getPlayers()->broadcastAll( shared_ptr( new ChatPacket(player->name, ChatPacket::e_ChatPlayerKickedFromGame) ) ); } else { server->getPlayers()->broadcastAll( shared_ptr( new ChatPacket(player->name, ChatPacket::e_ChatPlayerLeftGame) ) ); } server->getPlayers()->remove(player); done = true; LeaveCriticalSection(&done_cs); } void PlayerConnection::handlePlayerInput(shared_ptr packet) { player->setPlayerInput(packet->getXa(), packet->getYa(), packet->isJumping(), packet->isSneaking(), packet->getXRot(), packet->getYRot()); } void PlayerConnection::handleMovePlayer(shared_ptr packet) { ServerLevel *level = server->getLevel(player->dimension); didTick = true; if(synched) m_bHasClientTickedOnce = true; if (player->wonGame) return; if (!synched) { double yDiff = packet->y - yLastOk; if (packet->x == xLastOk && yDiff * yDiff < 0.01 && packet->z == zLastOk) { synched = true; } } if (synched) { if (player->riding != NULL) { float yRotT = player->yRot; float xRotT = player->xRot; player->riding->positionRider(); double xt = player->x; double yt = player->y; double zt = player->z; double xxa = 0; double zza = 0; if (packet->hasRot) { yRotT = packet->yRot; xRotT = packet->xRot; } if (packet->hasPos && packet->y == -999 && packet->yView == -999) { // CraftBukkit start if (abs(packet->x) > 1 || abs(packet->z) > 1) { //System.err.println(player.name + " was caught trying to crash the server with an invalid position."); #ifndef _CONTENT_PACKAGE wprintf(L"%ls was caught trying to crash the server with an invalid position.", player->name.c_str()); #endif disconnect(DisconnectPacket::eDisconnect_IllegalPosition);//"Nope!"); return; } // CraftBukkit end xxa = packet->x; zza = packet->z; } player->onGround = packet->onGround; player->doTick(false); player->move(xxa, 0, zza); player->absMoveTo(xt, yt, zt, yRotT, xRotT); player->xd = xxa; player->zd = zza; if (player->riding != NULL) level->forceTick(player->riding, true); if (player->riding != NULL) player->riding->positionRider(); server->getPlayers()->move(player); xLastOk = player->x; yLastOk = player->y; zLastOk = player->z; ((Level *)level)->tick(player); return; } if (player->isSleeping()) { player->doTick(false); player->absMoveTo(xLastOk, yLastOk, zLastOk, player->yRot, player->xRot); ((Level *)level)->tick(player); return; } double startY = player->y; xLastOk = player->x; yLastOk = player->y; zLastOk = player->z; double xt = player->x; double yt = player->y; double zt = player->z; float yRotT = player->yRot; float xRotT = player->xRot; if (packet->hasPos && packet->y == -999 && packet->yView == -999) { packet->hasPos = false; } if (packet->hasPos) { xt = packet->x; yt = packet->y; zt = packet->z; double yd = packet->yView - packet->y; if (!player->isSleeping() && (yd > 1.65 || yd < 0.1)) { disconnect(DisconnectPacket::eDisconnect_IllegalStance); // logger.warning(player->name + " had an illegal stance: " + yd); return; } if (abs(packet->x) > 32000000 || abs(packet->z) > 32000000) { disconnect(DisconnectPacket::eDisconnect_IllegalPosition); return; } } if (packet->hasRot) { yRotT = packet->yRot; xRotT = packet->xRot; } // 4J Stu Added to stop server player y pos being different than client when flying if(player->abilities.mayfly || player->isAllowedToFly() ) { player->abilities.flying = packet->isFlying; } else player->abilities.flying = false; player->doTick(false); player->ySlideOffset = 0; player->absMoveTo(xLastOk, yLastOk, zLastOk, yRotT, xRotT); if (!synched) return; double xDist = xt - player->x; double yDist = yt - player->y; double zDist = zt - player->z; double dist = xDist * xDist + yDist * yDist + zDist * zDist; // 4J-PB - removing this one for now /*if (dist > 100.0f) { // logger.warning(player->name + " moved too quickly!"); disconnect(DisconnectPacket::eDisconnect_MovedTooQuickly); // System.out.println("Moved too quickly at " + xt + ", " + yt + ", " + zt); // teleport(player->x, player->y, player->z, player->yRot, player->xRot); return; } */ float r = 1 / 16.0f; bool oldOk = level->getCubes(player, player->bb->copy()->shrink(r, r, r))->empty(); if (player->onGround && !packet->onGround && yDist > 0) { // assume the player made a jump player->causeFoodExhaustion(FoodConstants::EXHAUSTION_JUMP); } player->move(xDist, yDist, zDist); // 4J Stu - It is possible that we are no longer synched (eg By moving into an End Portal), so we should stop any further movement based on this packet // Fix for #87764 - Code: Gameplay: Host cannot move and experiences End World Chunks flickering, while in Splitscreen Mode // and Fix for #87788 - Code: Gameplay: Client cannot move and experiences End World Chunks flickering, while in Splitscreen Mode if (!synched) return; player->onGround = packet->onGround; // Since server players don't call travel we check food exhaustion // here player->checkMovementStatistiscs(xDist, yDist, zDist); double oyDist = yDist; xDist = xt - player->x; yDist = yt - player->y; // 4J-PB - line below will always be true! if (yDist > -0.5 || yDist < 0.5) { yDist = 0; } zDist = zt - player->z; dist = xDist * xDist + yDist * yDist + zDist * zDist; bool fail = false; if (dist > 0.25 * 0.25 && !player->isSleeping() && !player->gameMode->isCreative() && !player->isAllowedToFly()) { fail = true; // logger.warning(player->name + " moved wrongly!"); // System.out.println("Got position " + xt + ", " + yt + ", " + zt); // System.out.println("Expected " + player->x + ", " + player->y + ", " + player->z); #ifndef _CONTENT_PACKAGE wprintf(L"%ls moved wrongly!\n",player->name.c_str()); app.DebugPrintf("Got position %f, %f, %f\n", xt,yt,zt); app.DebugPrintf("Expected %f, %f, %f\n", player->x, player->y, player->z); #endif } player->absMoveTo(xt, yt, zt, yRotT, xRotT); bool newOk = level->getCubes(player, player->bb->copy()->shrink(r, r, r))->empty(); if (oldOk && (fail || !newOk) && !player->isSleeping()) { teleport(xLastOk, yLastOk, zLastOk, yRotT, xRotT); return; } AABB *testBox = player->bb->copy()->grow(r, r, r)->expand(0, -0.55, 0); // && server.level.getCubes(player, testBox).size() == 0 if (!server->isFlightAllowed() && !player->gameMode->isCreative() && !level->containsAnyBlocks(testBox) && !player->isAllowedToFly() ) { if (oyDist >= (-0.5f / 16.0f)) { aboveGroundTickCount++; if (aboveGroundTickCount > 80) { // logger.warning(player->name + " was kicked for floating too long!"); #ifndef _CONTENT_PACKAGE wprintf(L"%ls was kicked for floating too long!\n", player->name.c_str()); #endif disconnect(DisconnectPacket::eDisconnect_NoFlying); return; } } } else { aboveGroundTickCount = 0; } player->onGround = packet->onGround; server->getPlayers()->move(player); player->doCheckFallDamage(player->y - startY, packet->onGround); } } void PlayerConnection::teleport(double x, double y, double z, float yRot, float xRot, bool sendPacket /*= true*/) { synched = false; xLastOk = x; yLastOk = y; zLastOk = z; player->absMoveTo(x, y, z, yRot, xRot); // 4J - note that 1.62 is added to the height here as the client connection that receives this will presume it represents y + heightOffset at that end // This is different to the way that height is sent back to the server, where it represents the bottom of the player bounding volume if(sendPacket) player->connection->send( shared_ptr( new MovePlayerPacket::PosRot(x, y + 1.62f, y, z, yRot, xRot, false, false) ) ); } void PlayerConnection::handlePlayerAction(shared_ptr packet) { ServerLevel *level = server->getLevel(player->dimension); if (packet->action == PlayerActionPacket::DROP_ITEM) { player->drop(); return; } else if (packet->action == PlayerActionPacket::RELEASE_USE_ITEM) { player->releaseUsingItem(); return; } // 4J Stu - We don't have ops, so just use the levels setting bool canEditSpawn = level->canEditSpawn; // = level->dimension->id != 0 || server->players->isOp(player->name); bool shouldVerifyLocation = false; if (packet->action == PlayerActionPacket::START_DESTROY_BLOCK) shouldVerifyLocation = true; if (packet->action == PlayerActionPacket::STOP_DESTROY_BLOCK) shouldVerifyLocation = true; int x = packet->x; int y = packet->y; int z = packet->z; if (packet->action == PlayerActionPacket::START_DESTROY_BLOCK && IsBedwarsHubProtectedArea(level, x, y, z)) { player->connection->send(shared_ptr(new TileUpdatePacket(x, y, z, level))); return; } if (shouldVerifyLocation) { double xDist = player->x - (x + 0.5); // there is a mismatch between the player's camera and the player's // position, so add 1.5 blocks double yDist = player->y - (y + 0.5) + 1.5; double zDist = player->z - (z + 0.5); double dist = xDist * xDist + yDist * yDist + zDist * zDist; if (dist > 6 * 6) { return; } if (y >= server->getMaxBuildHeight()) { return; } } Pos *spawnPos = level->getSharedSpawnPos(); int xd = (int) Mth::abs((float)(x - spawnPos->x)); int zd = (int) Mth::abs((float)(z - spawnPos->z)); delete spawnPos; if (xd > zd) zd = xd; if (packet->action == PlayerActionPacket::START_DESTROY_BLOCK) { if (zd > 16 || canEditSpawn) player->gameMode->startDestroyBlock(x, y, z, packet->face); else player->connection->send( shared_ptr( new TileUpdatePacket(x, y, z, level) ) ); } else if (packet->action == PlayerActionPacket::STOP_DESTROY_BLOCK) { player->gameMode->stopDestroyBlock(x, y, z); server->getPlayers()->prioritiseTileChanges(x, y, z, level->dimension->id); // 4J added - make sure that the update packets for this get prioritised over other general world updates if (level->getTile(x, y, z) != 0) player->connection->send( shared_ptr( new TileUpdatePacket(x, y, z, level) ) ); } else if (packet->action == PlayerActionPacket::ABORT_DESTROY_BLOCK) { player->gameMode->abortDestroyBlock(x, y, z); if (level->getTile(x, y, z) != 0) player->connection->send(shared_ptr( new TileUpdatePacket(x, y, z, level))); } else if (packet->action == PlayerActionPacket::GET_UPDATED_BLOCK) { double xDist = player->x - (x + 0.5); double yDist = player->y - (y + 0.5); double zDist = player->z - (z + 0.5); double dist = xDist * xDist + yDist * yDist + zDist * zDist; if (dist < 16 * 16) { player->connection->send( shared_ptr( new TileUpdatePacket(x, y, z, level) ) ); } } // 4J Stu - Don't change the levels state //level->canEditSpawn = false; } void PlayerConnection::handleUseItem(shared_ptr packet) { ServerLevel *level = server->getLevel(player->dimension); shared_ptr item = player->inventory->getSelected(); bool informClient = false; int x = packet->getX(); int y = packet->getY(); int z = packet->getZ(); int face = packet->getFace(); if (face != 255 && IsBedwarsHubProtectedArea(level, x, y, z)) { player->connection->send(shared_ptr(new TileUpdatePacket(x, y, z, level))); return; } // 4J Stu - We don't have ops, so just use the levels setting bool canEditSpawn = level->canEditSpawn; // = level->dimension->id != 0 || server->players->isOp(player->name); if (packet->getFace() == 255) { if (item == NULL) return; player->gameMode->useItem(player, level, item); } else if ((packet->getY() < server->getMaxBuildHeight() - 1) || (packet->getFace() != Facing::UP && packet->getY() < server->getMaxBuildHeight())) { Pos *spawnPos = level->getSharedSpawnPos(); int xd = (int) Mth::abs((float)(x - spawnPos->x)); int zd = (int) Mth::abs((float)(z - spawnPos->z)); delete spawnPos; if (xd > zd) zd = xd; if (synched && player->distanceToSqr(x + 0.5, y + 0.5, z + 0.5) < 8 * 8) { if (zd > 16 || canEditSpawn) { player->gameMode->useItemOn(player, level, item, x, y, z, face, packet->getClickX(), packet->getClickY(), packet->getClickZ()); } } informClient = true; } else { //player->connection->send(shared_ptr(new ChatPacket("\u00A77Height limit for building is " + server->maxBuildHeight))); informClient = true; } if (informClient) { player->connection->send( shared_ptr( new TileUpdatePacket(x, y, z, level) ) ); if (face == 0) y--; if (face == 1) y++; if (face == 2) z--; if (face == 3) z++; if (face == 4) x--; if (face == 5) x++; // 4J - Fixes an issue where pistons briefly disappear when retracting. The pistons themselves shouldn't have their change from being pistonBase_Id to pistonMovingPiece_Id // directly sent to the client, as this will happen on the client as a result of it actioning (via a tile event) the retraction of the piston locally. However, by putting a switch // beside a piston and then performing an action on the side of it facing a piston, the following line of code will send a TileUpdatePacket containing the change to pistonMovingPiece_Id // to the client, and this packet is received before the piston retract action happens - when the piston retract then occurs, it doesn't work properly because the piston tile // isn't what it is expecting. if( level->getTile(x,y,z) != Tile::pistonMovingPiece_Id ) { player->connection->send( shared_ptr( new TileUpdatePacket(x, y, z, level) ) ); } } item = player->inventory->getSelected(); if (item != NULL && item->count == 0) { player->inventory->items[player->inventory->selected] = nullptr; item = nullptr; } if (item == NULL || item->getUseDuration() == 0) { player->ignoreSlotUpdateHack = true; player->inventory->items[player->inventory->selected] = ItemInstance::clone(player->inventory->items[player->inventory->selected]); Slot *s = player->containerMenu->getSlotFor(player->inventory, player->inventory->selected); player->containerMenu->broadcastChanges(); player->ignoreSlotUpdateHack = false; if (!ItemInstance::matches(player->inventory->getSelected(), packet->getItem())) { send( shared_ptr( new ContainerSetSlotPacket(player->containerMenu->containerId, s->index, player->inventory->getSelected()) ) ); } } // 4J Stu - Don't change the levels state //level->canEditSpawn = false; } void PlayerConnection::onDisconnect(DisconnectPacket::eDisconnectReason reason, void *reasonObjects) { EnterCriticalSection(&done_cs); if( done ) return; // logger.info(player.name + " lost connection: " + reason); // 4J-PB - removed, since it needs to be localised in the language the client is in //server->players->broadcastAll( shared_ptr( new ChatPacket(L"§e" + player->name + L" left the game.") ) ); if(getWasKicked()) { server->getPlayers()->broadcastAll( shared_ptr( new ChatPacket(player->name, ChatPacket::e_ChatPlayerKickedFromGame) ) ); } else { server->getPlayers()->broadcastAll( shared_ptr( new ChatPacket(player->name, ChatPacket::e_ChatPlayerLeftGame) ) ); } server->getPlayers()->remove(player); done = true; LeaveCriticalSection(&done_cs); } void PlayerConnection::onUnhandledPacket(shared_ptr packet) { // logger.warning(getClass() + " wasn't prepared to deal with a " + packet.getClass()); disconnect(DisconnectPacket::eDisconnect_UnexpectedPacket); } void PlayerConnection::send(shared_ptr packet) { if( connection->getSocket() != NULL ) { if( !server->getPlayers()->canReceiveAllPackets( player ) ) { // Check if we are allowed to send this packet type if( !Packet::canSendToAnyClient(packet) ) { //wprintf(L"Not the systems primary player, so not sending them a packet : %ls / %d\n", player->name.c_str(), packet->getId() ); return; } } connection->send(packet); } } // 4J Added void PlayerConnection::queueSend(shared_ptr packet) { if( connection->getSocket() != NULL ) { if( !server->getPlayers()->canReceiveAllPackets( player ) ) { // Check if we are allowed to send this packet type if( !Packet::canSendToAnyClient(packet) ) { //wprintf(L"Not the systems primary player, so not queueing them a packet : %ls\n", connection->getSocket()->getPlayer()->GetGamertag() ); return; } } connection->queueSend(packet); } } void PlayerConnection::handleSetCarriedItem(shared_ptr packet) { if (packet->slot < 0 || packet->slot >= Inventory::getSelectionSize()) { // logger.warning(player.name + " tried to set an invalid carried item"); return; } player->inventory->selected = packet->slot; } void PlayerConnection::handleChat(shared_ptr packet) { if(packet == NULL || packet->m_stringArgs.size() == 0) { return; } wstring message = packet->m_stringArgs[0]; if(message.length() > SharedConstants::maxChatLength) { disconnect(DisconnectPacket::eDisconnect_Overflow); return; } size_t first = 0; while(first < message.length() && std::iswspace(message[first])) { ++first; } size_t last = message.length(); while(last > first && std::iswspace(message[last - 1])) { --last; } message = message.substr(first, last - first); if(message.length() == 0) { return; } for(size_t i = 0; i < message.length(); ++i) { wchar_t ch = message[i]; if(ch < 32 && SharedConstants::acceptableLetters.find(ch) == wstring::npos) { return; } } if(message[0] == L'/') { handleCommand(message); return; } server->getPlayers()->broadcastAll(shared_ptr(new ChatPacket(player->name, ChatPacket::e_ChatCustom, -1, message))); chatSpamTickCount += SharedConstants::TICKS_PER_SECOND; if(chatSpamTickCount > SharedConstants::TICKS_PER_SECOND * 10) { disconnect(DisconnectPacket::eDisconnect_Overflow); } } void PlayerConnection::handleCommand(const wstring& message) { if (message.length() < 2 || message[0] != L'/') { return; } vector tokens = SplitCommandTokens(message.substr(1)); if (tokens.empty()) { return; } const wstring command = ToLowerText(tokens[0]); const BYTE selfSmallId = GetSmallIdForPlayer(player); INetworkPlayer *requestPlayer = getNetworkPlayer(); const bool isHost = (requestPlayer != NULL && requestPlayer->IsHost()); const bool isPersistedOp = (server != NULL && server->getPlayers() != NULL && server->getPlayers()->isOp(player->name)); const bool hasAdminPermission = isHost || player->isModerator() || isPersistedOp; if (command == L"tps") { float tps = server->getRecentTps(); wchar_t tpsText[64]; swprintf_s(tpsText, L"TPS: %.2f", tps); player->sendMessage(tpsText, ChatPacket::e_ChatCustom); return; } if (command == L"help") { player->sendMessage(L"/tps, /list, /party, /queue, /queuehost, /hub, /server", ChatPacket::e_ChatCustom); if (hasAdminPermission) { player->sendMessage(L"Admin: /kick /ban /pardon /op /deop /tp /gamemode /save-on /save-off /save-all /whitelist /send", ChatPacket::e_ChatCustom); } return; } if (command == L"list") { wchar_t listInfo[80]; const int count = (server != NULL && server->getPlayers() != NULL) ? server->getPlayers()->getPlayerCount() : 0; const int maxPlayers = (server != NULL && server->getPlayers() != NULL) ? server->getPlayers()->getMaxPlayers() : MINECRAFT_NET_MAX_PLAYERS; swprintf_s(listInfo, L"Players: %d/%d", count, maxPlayers); player->sendMessage(listInfo, ChatPacket::e_ChatCustom); if (server != NULL && server->getPlayers() != NULL) { const wstring names = server->getPlayers()->getPlayerNames(); if (!names.empty()) { player->sendMessage(names, ChatPacket::e_ChatCustom); } } return; } if (command == L"server") { if (tokens.size() < 2) { player->sendMessage(L"Usage: /server ", ChatPacket::e_ChatCustom); return; } const wstring sub = ToLowerText(tokens[1]); if (sub == L"list") { LoadProxyRoutes(false); if (s_proxyRoutes.empty()) { player->sendMessage(L"No proxy routes configured. Create proxy-worlds.properties.", ChatPacket::e_ChatCustom); return; } player->sendMessage(L"Available servers:", ChatPacket::e_ChatCustom); for (AUTO_VAR(itRoute, s_proxyRoutes.begin()); itRoute != s_proxyRoutes.end(); ++itRoute) { const ProxyRoute &route = itRoute->second; wstring line = L" - "; line += itRoute->first; line += L" ("; line += route.displayName; line += L")"; player->sendMessage(line, ChatPacket::e_ChatCustom); } return; } if (sub == L"reload") { if (!hasAdminPermission) { player->sendMessage(L"You do not have permission.", ChatPacket::e_ChatCustom); return; } LoadProxyRoutes(true); player->sendMessage(L"Proxy route config reloaded.", ChatPacket::e_ChatCustom); return; } ProxyRoute route; if (!TryGetProxyRoute(sub, route)) { player->sendMessage(L"Unknown server route. Use /server list.", ChatPacket::e_ChatCustom); return; } SendTransferPayloadEx(player, route.hostIp, route.hostPort, 0xFF, route.displayName); wstring msg = L"Transferring to "; msg += route.displayName; msg += L"..."; player->sendMessage(msg, ChatPacket::e_ChatCustom); return; } if (command == L"send") { if (!hasAdminPermission) { player->sendMessage(L"You do not have permission.", ChatPacket::e_ChatCustom); return; } if (tokens.size() < 3) { player->sendMessage(L"Usage: /send ", ChatPacket::e_ChatCustom); return; } shared_ptr target = FindPlayerByNameInsensitive(server, tokens[1]); if (target == NULL || target->connection == NULL) { player->sendMessage(L"Player not found.", ChatPacket::e_ChatCustom); return; } ProxyRoute route; if (!TryGetProxyRoute(tokens[2], route)) { player->sendMessage(L"Unknown server route. Use /server list.", ChatPacket::e_ChatCustom); return; } SendTransferPayloadEx(target, route.hostIp, route.hostPort, 0xFF, route.displayName); wstring adminMsg = L"Sent "; adminMsg += target->name; adminMsg += L" to "; adminMsg += route.displayName; player->sendMessage(adminMsg, ChatPacket::e_ChatCustom); return; } if (command == L"whitelist") { if (!hasAdminPermission) { player->sendMessage(L"You do not have permission.", ChatPacket::e_ChatCustom); return; } if (tokens.size() < 2) { player->sendMessage(L"Usage: /whitelist ", ChatPacket::e_ChatCustom); return; } const wstring sub = ToLowerText(tokens[1]); if (sub == L"on") { server->getPlayers()->setWhitelistEnabled(true); player->sendMessage(L"Whitelist enabled.", ChatPacket::e_ChatCustom); return; } if (sub == L"off") { server->getPlayers()->setWhitelistEnabled(false); player->sendMessage(L"Whitelist disabled.", ChatPacket::e_ChatCustom); return; } if (sub == L"reload") { server->getPlayers()->reloadWhitelist(); player->sendMessage(L"Whitelist reloaded.", ChatPacket::e_ChatCustom); return; } if (sub == L"add" && tokens.size() >= 3) { server->getPlayers()->whiteList(tokens[2]); wstring msg = L"Added to whitelist: "; msg += tokens[2]; player->sendMessage(msg, ChatPacket::e_ChatCustom); return; } if ((sub == L"remove" || sub == L"del") && tokens.size() >= 3) { server->getPlayers()->blackList(tokens[2]); wstring msg = L"Removed from whitelist: "; msg += tokens[2]; player->sendMessage(msg, ChatPacket::e_ChatCustom); return; } player->sendMessage(L"Usage: /whitelist ", ChatPacket::e_ChatCustom); return; } if (command == L"op" || command == L"deop") { if (!hasAdminPermission) { player->sendMessage(L"You do not have permission.", ChatPacket::e_ChatCustom); return; } if (tokens.size() < 2) { player->sendMessage(command == L"op" ? L"Usage: /op " : L"Usage: /deop ", ChatPacket::e_ChatCustom); return; } const bool isAdd = (command == L"op"); const wstring &targetName = tokens[1]; bool changed = isAdd ? server->getPlayers()->addOp(targetName) : server->getPlayers()->removeOp(targetName); shared_ptr target = FindPlayerByNameInsensitive(server, targetName); if (target != NULL) { target->setPlayerGamePrivilege(Player::ePlayerGamePrivilege_Op, isAdd ? 1 : 0); server->getPlayers()->broadcastAll(shared_ptr(new PlayerInfoPacket(target))); } if (changed) { wstring ok = (isAdd ? L"Granted op to " : L"Removed op from "); ok += targetName; player->sendMessage(ok, ChatPacket::e_ChatCustom); } else { wstring msg = (isAdd ? L"Already op: " : L"Not op: "); msg += targetName; player->sendMessage(msg, ChatPacket::e_ChatCustom); } return; } if (command == L"kick" || command == L"ban" || command == L"pardon") { if (!hasAdminPermission) { player->sendMessage(L"You do not have permission.", ChatPacket::e_ChatCustom); return; } if ((command == L"kick" || command == L"ban") && tokens.size() < 2) { player->sendMessage(command == L"kick" ? L"Usage: /kick " : L"Usage: /ban ", ChatPacket::e_ChatCustom); return; } if (command == L"pardon") { if (tokens.size() < 2) { player->sendMessage(L"Usage: /pardon ", ChatPacket::e_ChatCustom); return; } if (server->getPlayers()->unbanName(tokens[1])) { wstring msg = L"Unbanned "; msg += tokens[1]; player->sendMessage(msg, ChatPacket::e_ChatCustom); } else { player->sendMessage(L"Player was not banned.", ChatPacket::e_ChatCustom); } return; } shared_ptr target = FindPlayerByNameInsensitive(server, tokens[1]); if (target == NULL || target->connection == NULL) { player->sendMessage(L"Player not found.", ChatPacket::e_ChatCustom); return; } if (target == player) { player->sendMessage(L"You cannot target yourself.", ChatPacket::e_ChatCustom); return; } if (command == L"ban") { server->getPlayers()->banName(target->name); } target->connection->setWasKicked(); target->connection->disconnect(command == L"ban" ? DisconnectPacket::eDisconnect_Banned : DisconnectPacket::eDisconnect_Kicked); return; } if (command == L"save-on" || command == L"save-off" || command == L"save-all") { if (!hasAdminPermission) { player->sendMessage(L"You do not have permission.", ChatPacket::e_ChatCustom); return; } if (command == L"save-all") { server->getPlayers()->saveAll(NULL, false); player->sendMessage(L"Saved all player data.", ChatPacket::e_ChatCustom); return; } const bool disableSaving = (command == L"save-off"); app.SetGameHostOption(eGameHostOption_DisableSaving, disableSaving ? 1 : 0); player->sendMessage(disableSaving ? L"World saving disabled." : L"World saving enabled.", ChatPacket::e_ChatCustom); return; } if (command == L"gamemode" || command == L"gm") { if (!hasAdminPermission) { player->sendMessage(L"You do not have permission.", ChatPacket::e_ChatCustom); return; } if (tokens.size() < 2) { player->sendMessage(L"Usage: /gamemode [player]", ChatPacket::e_ChatCustom); return; } const wstring gmArg = ToLowerText(tokens[1]); GameType *gameType = NULL; if (gmArg == L"0" || gmArg == L"s" || gmArg == L"survival") { gameType = GameType::SURVIVAL; } else if (gmArg == L"1" || gmArg == L"c" || gmArg == L"creative") { gameType = GameType::CREATIVE; } if (gameType == NULL) { player->sendMessage(L"Unknown gamemode.", ChatPacket::e_ChatCustom); return; } shared_ptr target = player; if (tokens.size() >= 3) { target = FindPlayerByNameInsensitive(server, tokens[2]); if (target == NULL) { player->sendMessage(L"Player not found.", ChatPacket::e_ChatCustom); return; } } target->setPlayerGamePrivilege(Player::ePlayerGamePrivilege_CreativeMode, gameType == GameType::CREATIVE ? 1 : 0); target->gameMode->setGameModeForPlayer(gameType); target->connection->send(shared_ptr(new GameEventPacket(GameEventPacket::CHANGE_GAME_MODE, gameType->getId()))); server->getPlayers()->broadcastAll(shared_ptr(new PlayerInfoPacket(target))); player->sendMessage(L"Gamemode updated.", ChatPacket::e_ChatCustom); return; } if (command == L"tp") { if (!hasAdminPermission) { player->sendMessage(L"You do not have permission.", ChatPacket::e_ChatCustom); return; } if (tokens.size() < 2) { player->sendMessage(L"Usage: /tp [destination]", ChatPacket::e_ChatCustom); return; } shared_ptr toMove = player; shared_ptr destination = NULL; if (tokens.size() == 2) { destination = FindPlayerByNameInsensitive(server, tokens[1]); } else { toMove = FindPlayerByNameInsensitive(server, tokens[1]); destination = FindPlayerByNameInsensitive(server, tokens[2]); } if (toMove == NULL || destination == NULL || toMove->connection == NULL) { player->sendMessage(L"Player not found.", ChatPacket::e_ChatCustom); return; } toMove->connection->teleport(destination->x, destination->y, destination->z, destination->yRot, destination->xRot); player->sendMessage(L"Teleported.", ChatPacket::e_ChatCustom); return; } if (command == L"hub" && IsBedwarsHubSettings(app.GetGameHostOption(eGameHostOption_All))) { ServerLevel *level = server->getLevel(player->dimension); if (level != NULL) { Pos *spawnPos = level->getSharedSpawnPos(); if (spawnPos != NULL) { teleport((double)spawnPos->x + 0.5, (double)spawnPos->y + 1.0, (double)spawnPos->z + 0.5, 0.0f, 0.0f); delete spawnPos; } } player->sendMessage(L"Returned to Bedwars hub.", ChatPacket::e_ChatCustom); return; } if (command == L"queue" && IsBedwarsHostSettings(app.GetGameHostOption(eGameHostOption_All))) { if (!IsBedwarsHubSettings(app.GetGameHostOption(eGameHostOption_All))) { player->sendMessage(L"Queues are only available in Bedwars hub mode.", ChatPacket::e_ChatCustom); return; } if (tokens.size() < 2) { player->sendMessage(L"Usage: /queue ", ChatPacket::e_ChatCustom); return; } const wstring queueArg = ToLowerText(tokens[1]); if (queueArg == L"leave") { RemovePlayerFromAllQueues(server, selfSmallId, true, L"left"); return; } const int queueIndex = GetQueueIndexFromName(queueArg); if (queueIndex < 0) { player->sendMessage(L"Unknown queue. Use solo, doubles, squads or practice.", ChatPacket::e_ChatCustom); return; } JoinBedwarsQueue(server, player, queueIndex); return; } if (command == L"queuehost" && IsBedwarsHostSettings(app.GetGameHostOption(eGameHostOption_All))) { INetworkPlayer *networkPlayer = getNetworkPlayer(); if (networkPlayer == NULL || !networkPlayer->IsHost()) { player->sendMessage(L"Only the host can change queue hosting mode.", ChatPacket::e_ChatCustom); return; } if (tokens.size() < 2) { player->sendMessage(L"Usage: /queuehost ", ChatPacket::e_ChatCustom); return; } const wstring hostMode = ToLowerText(tokens[1]); unsigned int hostSettings = app.GetGameHostOption(eGameHostOption_All); if (hostMode == L"hub") { hostSettings = ApplyBedwarsSessionMetadata(hostSettings, MINIGAME_ROLE_HUB, 0u); app.SetGameHostOption(eGameHostOption_All, hostSettings); g_NetworkManager.UpdateAndSetGameSessionData(); player->sendMessage(L"This server is now a Bedwars hub.", ChatPacket::e_ChatCustom); return; } const int queueIndex = GetQueueIndexFromName(hostMode); if (queueIndex < 0) { player->sendMessage(L"Unknown queue mode for /queuehost.", ChatPacket::e_ChatCustom); return; } hostSettings = ApplyBedwarsSessionMetadata(hostSettings, MINIGAME_ROLE_MATCH, (unsigned int)queueIndex); app.SetGameHostOption(eGameHostOption_All, hostSettings); g_NetworkManager.UpdateAndSetGameSessionData(); wstring modeMsg = L"This server now accepts "; modeMsg += GetQueueDisplayName(queueIndex); modeMsg += L" queue transfers."; player->sendMessage(modeMsg, ChatPacket::e_ChatCustom); return; } if (command == L"party") { if (selfSmallId == 0xFF) { return; } if (tokens.size() < 2) { player->sendMessage(L"Usage: /party ", ChatPacket::e_ChatCustom); return; } const wstring sub = ToLowerText(tokens[1]); if (sub == L"invite") { if (tokens.size() < 3) { player->sendMessage(L"Usage: /party invite ", ChatPacket::e_ChatCustom); return; } const BYTE currentLeader = GetPartyLeaderForMember(selfSmallId); if (currentLeader != selfSmallId) { player->sendMessage(L"Only the party leader can invite players.", ChatPacket::e_ChatCustom); return; } shared_ptr target = FindPlayerByNameInsensitive(server, tokens[2]); if (target == NULL) { player->sendMessage(L"Player not found.", ChatPacket::e_ChatCustom); return; } const BYTE targetSmallId = GetSmallIdForPlayer(target); if (targetSmallId == 0xFF || targetSmallId == selfSmallId) { player->sendMessage(L"Invalid party target.", ChatPacket::e_ChatCustom); return; } PartyInvite invite; invite.leaderSmallId = selfSmallId; invite.expireTick = server->tickCount + PARTY_INVITE_TICKS; s_partyInvites[targetSmallId] = invite; wstring sentMsg = L"Invited "; sentMsg += target->name; sentMsg += L" to your party."; player->sendMessage(sentMsg, ChatPacket::e_ChatCustom); wstring recvMsg = player->name; recvMsg += L" invited you to a party. Type /party accept."; SendPlayerMessage(target, recvMsg); return; } if (sub == L"accept") { auto inviteIt = s_partyInvites.find(selfSmallId); if (inviteIt == s_partyInvites.end()) { player->sendMessage(L"You have no pending party invite.", ChatPacket::e_ChatCustom); return; } const BYTE leaderSmallId = inviteIt->second.leaderSmallId; shared_ptr leader = FindPlayerBySmallId(server, leaderSmallId); if (leader == NULL) { s_partyInvites.erase(selfSmallId); player->sendMessage(L"That party invite has expired.", ChatPacket::e_ChatCustom); return; } RemovePlayerFromAllQueues(server, selfSmallId, true, L"party updated"); LeaveParty(server, selfSmallId, false); EnsurePartyLeaderEntry(leaderSmallId); s_partyMembersByLeader[leaderSmallId].insert(selfSmallId); s_partyLeaderByMember[selfSmallId] = leaderSmallId; s_partyInvites.erase(selfSmallId); wstring joinedMsg = L"You joined "; joinedMsg += leader->name; joinedMsg += L"'s party."; player->sendMessage(joinedMsg, ChatPacket::e_ChatCustom); wstring leaderMsg = player->name; leaderMsg += L" joined your party."; SendPlayerMessage(leader, leaderMsg); return; } if (sub == L"leave") { RemovePlayerFromAllQueues(server, selfSmallId, true, L"party left"); LeaveParty(server, selfSmallId, true); return; } if (sub == L"list") { const BYTE leaderSmallId = GetPartyLeaderForMember(selfSmallId); auto partyIt = s_partyMembersByLeader.find(leaderSmallId); if (partyIt == s_partyMembersByLeader.end() || partyIt->second.size() <= 1) { player->sendMessage(L"You are not in a party.", ChatPacket::e_ChatCustom); return; } wstring listMsg = L"Party: "; bool first = true; for (AUTO_VAR(itM, partyIt->second.begin()); itM != partyIt->second.end(); ++itM) { if (!first) listMsg += L", "; listMsg += GetPlayerNameBySmallId(server, *itM); first = false; } player->sendMessage(listMsg, ChatPacket::e_ChatCustom); return; } player->sendMessage(L"Usage: /party ", ChatPacket::e_ChatCustom); return; } player->sendMessage(L"Unknown command. Use /help.", ChatPacket::e_ChatCustom); } void PlayerConnection::handleAnimate(shared_ptr packet) { if (packet->action == AnimatePacket::SWING) { player->swing(); } } void PlayerConnection::handlePlayerCommand(shared_ptr packet) { if (packet->action == PlayerCommandPacket::START_SNEAKING) { player->setSneaking(true); } else if (packet->action == PlayerCommandPacket::STOP_SNEAKING) { player->setSneaking(false); } else if (packet->action == PlayerCommandPacket::START_SPRINTING) { player->setSprinting(true); } else if (packet->action == PlayerCommandPacket::STOP_SPRINTING) { player->setSprinting(false); } else if (packet->action == PlayerCommandPacket::STOP_SLEEPING) { player->stopSleepInBed(false, true, true); synched = false; } else if (packet->action == PlayerCommandPacket::START_IDLEANIM) { player->setIsIdle(true); } else if (packet->action == PlayerCommandPacket::STOP_IDLEANIM) { player->setIsIdle(false); } } void PlayerConnection::setShowOnMaps(bool bVal) { player->setShowOnMaps(bVal); } void PlayerConnection::handleDisconnect(shared_ptr packet) { // 4J Stu - Need to remove the player from the receiving list before their socket is NULLed so that we can find another player on their system ClearPlayerStateOnDisconnect(server, player); server->getPlayers()->removePlayerFromReceiving( player ); connection->close(DisconnectPacket::eDisconnect_Quitting); } int PlayerConnection::countDelayedPackets() { return connection->countDelayedPackets(); } void PlayerConnection::info(const wstring& string) { // 4J-PB - removed, since it needs to be localised in the language the client is in //send( shared_ptr( new ChatPacket(L"§7" + string) ) ); } void PlayerConnection::warn(const wstring& string) { // 4J-PB - removed, since it needs to be localised in the language the client is in //send( shared_ptr( new ChatPacket(L"§9" + string) ) ); } wstring PlayerConnection::getConsoleName() { return player->name; } void PlayerConnection::handleInteract(shared_ptr packet) { ServerLevel *level = server->getLevel(player->dimension); shared_ptr target = level->getEntity(packet->target); // Fix for #8218 - Gameplay: Attacking zombies from a different level often results in no hits being registered // 4J Stu - If the client says that we hit something, then agree with it. The canSee can fail here as it checks // a ray from head->head, but we may actually be looking at a different part of the entity that can be seen // even though the ray is blocked. if (target != NULL) // && player->canSee(target) && player->distanceToSqr(target) < 6 * 6) { if (packet->action == InteractPacket::INTERACT && IsBedwarsHubSettings(app.GetGameHostOption(eGameHostOption_All)) && target->GetType() == eTYPE_VILLAGER) { const int queueIndex = FindBedwarsQueueIndexForTarget(level, target); if (queueIndex >= 0) { Pos *spawnPos = level->getSharedSpawnPos(); if (spawnPos != NULL) { const BedwarsQueueDef &queue = BEDWARS_QUEUE_DEFS[queueIndex]; const double queueX = (double)spawnPos->x + 0.5 + (double)queue.queueOffX; const double queueY = (double)spawnPos->y + 1.0; const double queueZ = (double)spawnPos->z + 0.5 + (double)queue.queueOffZ; delete spawnPos; teleport(queueX, queueY, queueZ, queue.queueYaw, 0.0f); } JoinBedwarsQueue(server, player, queueIndex); } else { send(shared_ptr(new ChatPacket(L"Bedwars", ChatPacket::e_ChatCustom, -1, L"This Bedwars NPC is unavailable."))); } return; } //boole canSee = player->canSee(target); //double maxDist = 6 * 6; //if (!canSee) //{ // maxDist = 3 * 3; //} //if (player->distanceToSqr(target) < maxDist) //{ if (packet->action == InteractPacket::INTERACT) { player->interact(target); } else if (packet->action == InteractPacket::ATTACK) { player->attack(target); } //} } } bool PlayerConnection::canHandleAsyncPackets() { return true; } void PlayerConnection::handleTexture(shared_ptr packet) { // Both PlayerConnection and ClientConnection should handle this mostly the same way if(packet->dwBytes==0) { // Request for texture #ifndef _CONTENT_PACKAGE wprintf(L"Server received request for custom texture %ls\n",packet->textureName.c_str()); #endif PBYTE pbData=NULL; DWORD dwBytes=0; app.GetMemFileDetails(packet->textureName,&pbData,&dwBytes); if(dwBytes!=0) { send( shared_ptr( new TexturePacket(packet->textureName,pbData,dwBytes) ) ); } else { m_texturesRequested.push_back( packet->textureName ); } } else { // Response with texture data #ifndef _CONTENT_PACKAGE wprintf(L"Server received custom texture %ls\n",packet->textureName.c_str()); #endif app.AddMemoryTextureFile(packet->textureName,packet->pbData,packet->dwBytes); server->connection->handleTextureReceived(packet->textureName); } } void PlayerConnection::handleTextureAndGeometry(shared_ptr packet) { // Both PlayerConnection and ClientConnection should handle this mostly the same way if(packet->dwTextureBytes==0) { // Request for texture and geometry #ifndef _CONTENT_PACKAGE wprintf(L"Server received request for custom texture %ls\n",packet->textureName.c_str()); #endif PBYTE pbData=NULL; DWORD dwTextureBytes=0; app.GetMemFileDetails(packet->textureName,&pbData,&dwTextureBytes); DLCSkinFile *pDLCSkinFile = app.m_dlcManager.getSkinFile(packet->textureName); if(dwTextureBytes!=0) { if(pDLCSkinFile) { if(pDLCSkinFile->getAdditionalBoxesCount()!=0) { send( shared_ptr( new TextureAndGeometryPacket(packet->textureName,pbData,dwTextureBytes,pDLCSkinFile) ) ); } else { send( shared_ptr( new TextureAndGeometryPacket(packet->textureName,pbData,dwTextureBytes) ) ); } } else { // we don't have the dlc skin, so retrieve the data from the app store vector *pvSkinBoxes = app.GetAdditionalSkinBoxes(packet->dwSkinID); unsigned int uiAnimOverrideBitmask= app.GetAnimOverrideBitmask(packet->dwSkinID); send( shared_ptr( new TextureAndGeometryPacket(packet->textureName,pbData,dwTextureBytes,pvSkinBoxes,uiAnimOverrideBitmask) ) ); } } else { m_texturesRequested.push_back( packet->textureName ); } } else { // Response with texture and geometry data #ifndef _CONTENT_PACKAGE wprintf(L"Server received custom texture %ls and geometry\n",packet->textureName.c_str()); #endif app.AddMemoryTextureFile(packet->textureName,packet->pbData,packet->dwTextureBytes); // add the geometry to the app list if(packet->dwBoxC!=0) { #ifndef _CONTENT_PACKAGE wprintf(L"Adding skin boxes for skin id %X, box count %d\n",packet->dwSkinID,packet->dwBoxC); #endif app.SetAdditionalSkinBoxes(packet->dwSkinID,packet->BoxDataA,packet->dwBoxC); } // Add the anim override app.SetAnimOverrideBitmask(packet->dwSkinID,packet->uiAnimOverrideBitmask); player->setCustomSkin(packet->dwSkinID); server->connection->handleTextureAndGeometryReceived(packet->textureName); } } void PlayerConnection::handleTextureReceived(const wstring &textureName) { // This sends the server received texture out to any other players waiting for the data AUTO_VAR(it, find( m_texturesRequested.begin(), m_texturesRequested.end(), textureName )); if( it != m_texturesRequested.end() ) { PBYTE pbData=NULL; DWORD dwBytes=0; app.GetMemFileDetails(textureName,&pbData,&dwBytes); if(dwBytes!=0) { send( shared_ptr( new TexturePacket(textureName,pbData,dwBytes) ) ); m_texturesRequested.erase(it); } } } void PlayerConnection::handleTextureAndGeometryReceived(const wstring &textureName) { // This sends the server received texture out to any other players waiting for the data AUTO_VAR(it, find( m_texturesRequested.begin(), m_texturesRequested.end(), textureName )); if( it != m_texturesRequested.end() ) { PBYTE pbData=NULL; DWORD dwTextureBytes=0; app.GetMemFileDetails(textureName,&pbData,&dwTextureBytes); DLCSkinFile *pDLCSkinFile=app.m_dlcManager.getSkinFile(textureName); if(dwTextureBytes!=0) { if(pDLCSkinFile && (pDLCSkinFile->getAdditionalBoxesCount()!=0)) { send( shared_ptr( new TextureAndGeometryPacket(textureName,pbData,dwTextureBytes,pDLCSkinFile) ) ); } else { // get the data from the app DWORD dwSkinID = app.getSkinIdFromPath(textureName); vector *pvSkinBoxes = app.GetAdditionalSkinBoxes(dwSkinID); unsigned int uiAnimOverrideBitmask= app.GetAnimOverrideBitmask(dwSkinID); send( shared_ptr( new TextureAndGeometryPacket(textureName,pbData,dwTextureBytes, pvSkinBoxes, uiAnimOverrideBitmask) ) ); } m_texturesRequested.erase(it); } } } void PlayerConnection::handleTextureChange(shared_ptr packet) { switch(packet->action) { case TextureChangePacket::e_TextureChange_Skin: player->setCustomSkin( app.getSkinIdFromPath( packet->path ) ); #ifndef _CONTENT_PACKAGE wprintf(L"Skin for server player %ls has changed to %ls (%d)\n", player->name.c_str(), player->customTextureUrl.c_str(), player->getPlayerDefaultSkin() ); #endif break; case TextureChangePacket::e_TextureChange_Cape: player->setCustomCape( Player::getCapeIdFromPath( packet->path ) ); //player->customTextureUrl2 = packet->path; #ifndef _CONTENT_PACKAGE wprintf(L"Cape for server player %ls has changed to %ls\n", player->name.c_str(), player->customTextureUrl2.c_str() ); #endif break; } if(!packet->path.empty() && packet->path.substr(0,3).compare(L"def") != 0 && !app.IsFileInMemoryTextures(packet->path)) { if( server->connection->addPendingTextureRequest(packet->path)) { #ifndef _CONTENT_PACKAGE wprintf(L"Sending texture packet to get custom skin %ls from player %ls\n",packet->path.c_str(), player->name.c_str()); #endif send(shared_ptr( new TexturePacket(packet->path,NULL,0) ) ); } } else if(!packet->path.empty() && app.IsFileInMemoryTextures(packet->path)) { // Update the ref count on the memory texture data app.AddMemoryTextureFile(packet->path,NULL,0); } server->getPlayers()->broadcastAll( shared_ptr( new TextureChangePacket(player,packet->action,packet->path) ), player->dimension ); } void PlayerConnection::handleTextureAndGeometryChange(shared_ptr packet) { player->setCustomSkin( app.getSkinIdFromPath( packet->path ) ); #ifndef _CONTENT_PACKAGE wprintf(L"PlayerConnection::handleTextureAndGeometryChange - Skin for server player %ls has changed to %ls (%d)\n", player->name.c_str(), player->customTextureUrl.c_str(), player->getPlayerDefaultSkin() ); #endif if(!packet->path.empty() && packet->path.substr(0,3).compare(L"def") != 0 && !app.IsFileInMemoryTextures(packet->path)) { if( server->connection->addPendingTextureRequest(packet->path)) { #ifndef _CONTENT_PACKAGE wprintf(L"Sending texture packet to get custom skin %ls from player %ls\n",packet->path.c_str(), player->name.c_str()); #endif send(shared_ptr( new TextureAndGeometryPacket(packet->path,NULL,0) ) ); } } else if(!packet->path.empty() && app.IsFileInMemoryTextures(packet->path)) { // Update the ref count on the memory texture data app.AddMemoryTextureFile(packet->path,NULL,0); player->setCustomSkin(packet->dwSkinID); // If we already have the texture, then we already have the model parts too //app.SetAdditionalSkinBoxes(packet->dwSkinID,) //DebugBreak(); } server->getPlayers()->broadcastAll( shared_ptr( new TextureAndGeometryChangePacket(player,packet->path) ), player->dimension ); } void PlayerConnection::handleServerSettingsChanged(shared_ptr packet) { if(packet->action==ServerSettingsChangedPacket::HOST_IN_GAME_SETTINGS) { // Need to check that this player has permission to change each individual setting? INetworkPlayer *networkPlayer = getNetworkPlayer(); if( (networkPlayer != NULL && networkPlayer->IsHost()) || player->isModerator()) { app.SetGameHostOption(eGameHostOption_FireSpreads, app.GetGameHostOption(packet->data,eGameHostOption_FireSpreads)); app.SetGameHostOption(eGameHostOption_TNT, app.GetGameHostOption(packet->data,eGameHostOption_TNT)); server->getPlayers()->broadcastAll( shared_ptr( new ServerSettingsChangedPacket( ServerSettingsChangedPacket::HOST_IN_GAME_SETTINGS,app.GetGameHostOption(eGameHostOption_All) ) ) ); // Update the QoS data g_NetworkManager.UpdateAndSetGameSessionData(); } } } void PlayerConnection::handleKickPlayer(shared_ptr packet) { INetworkPlayer *networkPlayer = getNetworkPlayer(); if( (networkPlayer != NULL && networkPlayer->IsHost()) || player->isModerator()) { server->getPlayers()->kickPlayerByShortId(packet->m_networkSmallId); } } void PlayerConnection::handleGameCommand(shared_ptr packet) { MinecraftServer::getInstance()->getCommandDispatcher()->performCommand(player, packet->command, packet->data); } void PlayerConnection::handleClientCommand(shared_ptr packet) { if (packet->action == ClientCommandPacket::PERFORM_RESPAWN) { if (player->wonGame) { player = server->getPlayers()->respawn(player, player->m_enteredEndExitPortal?0:player->dimension, true); } //else if (player.getLevel().getLevelData().isHardcore()) //{ // if (server.isSingleplayer() && player.name.equals(server.getSingleplayerName())) // { // player.connection.disconnect("You have died. Game over, man, it's game over!"); // server.selfDestruct(); // } // else // { // BanEntry ban = new BanEntry(player.name); // ban.setReason("Death in Hardcore"); // server.getPlayers().getBans().add(ban); // player.connection.disconnect("You have died. Game over, man, it's game over!"); // } //} else { if (player->getHealth() > 0) return; player = server->getPlayers()->respawn(player, 0, false); } } } void PlayerConnection::handleRespawn(shared_ptr packet) { } void PlayerConnection::handleContainerClose(shared_ptr packet) { player->doCloseContainer(); } #ifndef _CONTENT_PACKAGE void PlayerConnection::handleContainerSetSlot(shared_ptr packet) { if (packet->containerId == AbstractContainerMenu::CONTAINER_ID_CARRIED ) { player->inventory->setCarried(packet->item); } else { if (packet->containerId == AbstractContainerMenu::CONTAINER_ID_INVENTORY && packet->slot >= 36 && packet->slot < 36 + 9) { shared_ptr lastItem = player->inventoryMenu->getSlot(packet->slot)->getItem(); if (packet->item != NULL) { if (lastItem == NULL || lastItem->count < packet->item->count) { packet->item->popTime = Inventory::POP_TIME_DURATION; } } player->inventoryMenu->setItem(packet->slot, packet->item); player->ignoreSlotUpdateHack = true; player->containerMenu->broadcastChanges(); player->broadcastCarriedItem(); player->ignoreSlotUpdateHack = false; } else if (packet->containerId == player->containerMenu->containerId) { player->containerMenu->setItem(packet->slot, packet->item); player->ignoreSlotUpdateHack = true; player->containerMenu->broadcastChanges(); player->broadcastCarriedItem(); player->ignoreSlotUpdateHack = false; } } } #endif void PlayerConnection::handleContainerClick(shared_ptr packet) { if (player->containerMenu->containerId == packet->containerId && player->containerMenu->isSynched(player)) { shared_ptr clicked = player->containerMenu->clicked(packet->slotNum, packet->buttonNum, packet->quickKey?AbstractContainerMenu::CLICK_QUICK_MOVE:AbstractContainerMenu::CLICK_PICKUP, player); if (ItemInstance::matches(packet->item, clicked)) { // Yep, you sure did click what you claimed to click! player->connection->send( shared_ptr( new ContainerAckPacket(packet->containerId, packet->uid, true) ) ); player->ignoreSlotUpdateHack = true; player->containerMenu->broadcastChanges(); player->broadcastCarriedItem(); player->ignoreSlotUpdateHack = false; } else { // No, you clicked the wrong thing! expectedAcks[player->containerMenu->containerId] = packet->uid; player->connection->send( shared_ptr( new ContainerAckPacket(packet->containerId, packet->uid, false) ) ); player->containerMenu->setSynched(player, false); vector > items; for (unsigned int i = 0; i < player->containerMenu->slots->size(); i++) { items.push_back(player->containerMenu->slots->at(i)->getItem()); } player->refreshContainer(player->containerMenu, &items); // player.containerMenu.broadcastChanges(); } } } void PlayerConnection::handleContainerButtonClick(shared_ptr packet) { if (player->containerMenu->containerId == packet->containerId && player->containerMenu->isSynched(player)) { player->containerMenu->clickMenuButton(player, packet->buttonId); player->containerMenu->broadcastChanges(); } } void PlayerConnection::handleSetCreativeModeSlot(shared_ptr packet) { if (player->gameMode->isCreative()) { bool drop = packet->slotNum < 0; shared_ptr item = packet->item; if(item != NULL && item->id == Item::map_Id) { int mapScale = 3; #ifdef _LARGE_WORLDS int scale = MapItemSavedData::MAP_SIZE * 2 * (1 << mapScale); int centreXC = (int) (Math::round(player->x / scale) * scale); int centreZC = (int) (Math::round(player->z / scale) * scale); #else // 4J-PB - for Xbox maps, we'll centre them on the origin of the world, since we can fit the whole world in our map int centreXC = 0; int centreZC = 0; #endif item->setAuxValue( player->level->getAuxValueForMap(player->getXuid(), player->dimension, centreXC, centreZC, mapScale) ); shared_ptr data = MapItem::getSavedData(item->getAuxValue(), player->level); // 4J Stu - We only have one map per player per dimension, so don't reset the one that they have // when a new one is created wchar_t buf[64]; swprintf(buf,64,L"map_%d", item->getAuxValue()); std::wstring id = wstring(buf); if( data == NULL ) { data = shared_ptr( new MapItemSavedData(id) ); } player->level->setSavedData(id, (shared_ptr ) data); data->scale = mapScale; // 4J-PB - for Xbox maps, we'll centre them on the origin of the world, since we can fit the whole world in our map data->x = centreXC; data->z = centreZC; data->dimension = (byte) player->level->dimension->id; data->setDirty(); } bool validSlot = (packet->slotNum >= InventoryMenu::CRAFT_SLOT_START && packet->slotNum < (InventoryMenu::USE_ROW_SLOT_START + Inventory::getSelectionSize())); bool validItem = item == NULL || (item->id < Item::items.length && item->id >= 0 && Item::items[item->id] != NULL); bool validData = item == NULL || (item->getAuxValue() >= 0 && item->count > 0 && item->count <= 64); if (validSlot && validItem && validData) { if (item == NULL) { player->inventoryMenu->setItem(packet->slotNum, nullptr); } else { player->inventoryMenu->setItem(packet->slotNum, item ); } player->inventoryMenu->setSynched(player, true); // player.slotChanged(player.inventoryMenu, packet.slotNum, player.inventoryMenu.getSlot(packet.slotNum).getItem()); } else if (drop && validItem && validData) { if (dropSpamTickCount < SharedConstants::TICKS_PER_SECOND * 10) { dropSpamTickCount += SharedConstants::TICKS_PER_SECOND; // drop item shared_ptr dropped = player->drop(item); if (dropped != NULL) { dropped->setShortLifeTime(); } } } if( item != NULL && item->id == Item::map_Id ) { // 4J Stu - Maps need to have their aux value update, so the client should always be assumed to be wrong // This is how the Java works, as the client also incorrectly predicts the auxvalue of the mapItem vector > items; for (unsigned int i = 0; i < player->inventoryMenu->slots->size(); i++) { items.push_back(player->inventoryMenu->slots->at(i)->getItem()); } player->refreshContainer(player->inventoryMenu, &items); } } } void PlayerConnection::handleContainerAck(shared_ptr packet) { AUTO_VAR(it, expectedAcks.find(player->containerMenu->containerId)); if (it != expectedAcks.end() && packet->uid == it->second && player->containerMenu->containerId == packet->containerId && !player->containerMenu->isSynched(player)) { player->containerMenu->setSynched(player, true); } } void PlayerConnection::handleSignUpdate(shared_ptr packet) { app.DebugPrintf("PlayerConnection::handleSignUpdate\n"); ServerLevel *level = server->getLevel(player->dimension); if (level->hasChunkAt(packet->x, packet->y, packet->z)) { shared_ptr te = level->getTileEntity(packet->x, packet->y, packet->z); if (dynamic_pointer_cast(te) != NULL) { shared_ptr ste = dynamic_pointer_cast(te); if (!ste->isEditable()) { server->warn(L"Player " + player->name + L" just tried to change non-editable sign"); return; } } // 4J-JEV: Changed to allow characters to display as a []. if (dynamic_pointer_cast(te) != NULL) { int x = packet->x; int y = packet->y; int z = packet->z; shared_ptr ste = dynamic_pointer_cast(te); for (int i = 0; i < 4; i++) { wstring lineText = packet->lines[i].substr(0,15); ste->SetMessage( i, lineText ); } ste->SetVerified(false); ste->setChanged(); level->sendTileUpdated(x, y, z); } } } void PlayerConnection::handleKeepAlive(shared_ptr packet) { if (packet->id == lastKeepAliveId) { int time = (int) (System::nanoTime() / 1000000 - lastKeepAliveTime); player->latency = (player->latency * 3 + time) / 4; } } void PlayerConnection::handlePlayerInfo(shared_ptr packet) { // Need to check that this player has permission to change each individual setting? INetworkPlayer *networkPlayer = getNetworkPlayer(); if( (networkPlayer != NULL && networkPlayer->IsHost()) || player->isModerator() ) { shared_ptr serverPlayer; // Find the player being edited for(AUTO_VAR(it, server->getPlayers()->players.begin()); it != server->getPlayers()->players.end(); ++it) { shared_ptr checkingPlayer = *it; if(checkingPlayer->connection->getNetworkPlayer() != NULL && checkingPlayer->connection->getNetworkPlayer()->GetSmallId() == packet->m_networkSmallId) { serverPlayer = checkingPlayer; break; } } if(serverPlayer != NULL) { unsigned int origPrivs = serverPlayer->getAllPlayerGamePrivileges(); bool trustPlayers = app.GetGameHostOption(eGameHostOption_TrustPlayers) != 0; bool cheats = app.GetGameHostOption(eGameHostOption_CheatsEnabled) != 0; if(serverPlayer == player) { GameType *gameType = Player::getPlayerGamePrivilege(packet->m_playerPrivileges,Player::ePlayerGamePrivilege_CreativeMode) ? GameType::CREATIVE : GameType::SURVIVAL; gameType = LevelSettings::validateGameType(gameType->getId()); if (serverPlayer->gameMode->getGameModeForPlayer() != gameType) { #ifndef _CONTENT_PACKAGE wprintf(L"Setting %ls to game mode %d\n", serverPlayer->name.c_str(), gameType); #endif serverPlayer->setPlayerGamePrivilege(Player::ePlayerGamePrivilege_CreativeMode,Player::getPlayerGamePrivilege(packet->m_playerPrivileges,Player::ePlayerGamePrivilege_CreativeMode) ); serverPlayer->gameMode->setGameModeForPlayer(gameType); serverPlayer->connection->send( shared_ptr( new GameEventPacket(GameEventPacket::CHANGE_GAME_MODE, gameType->getId()) )); } else { #ifndef _CONTENT_PACKAGE wprintf(L"%ls already has game mode %d\n", serverPlayer->name.c_str(), gameType); #endif } if(cheats) { // Editing self bool canBeInvisible = Player::getPlayerGamePrivilege(origPrivs, Player::ePlayerGamePrivilege_CanToggleInvisible) != 0; if(canBeInvisible)serverPlayer->setPlayerGamePrivilege(Player::ePlayerGamePrivilege_Invisible,Player::getPlayerGamePrivilege(packet->m_playerPrivileges,Player::ePlayerGamePrivilege_Invisible) ); if(canBeInvisible)serverPlayer->setPlayerGamePrivilege(Player::ePlayerGamePrivilege_Invulnerable,Player::getPlayerGamePrivilege(packet->m_playerPrivileges,Player::ePlayerGamePrivilege_Invulnerable) ); bool inCreativeMode = Player::getPlayerGamePrivilege(origPrivs,Player::ePlayerGamePrivilege_CreativeMode) != 0; if(!inCreativeMode) { bool canFly = Player::getPlayerGamePrivilege(origPrivs,Player::ePlayerGamePrivilege_CanToggleFly); bool canChangeHunger = Player::getPlayerGamePrivilege(origPrivs,Player::ePlayerGamePrivilege_CanToggleClassicHunger); if(canFly)serverPlayer->setPlayerGamePrivilege(Player::ePlayerGamePrivilege_CanFly,Player::getPlayerGamePrivilege(packet->m_playerPrivileges,Player::ePlayerGamePrivilege_CanFly) ); if(canChangeHunger)serverPlayer->setPlayerGamePrivilege(Player::ePlayerGamePrivilege_ClassicHunger,Player::getPlayerGamePrivilege(packet->m_playerPrivileges,Player::ePlayerGamePrivilege_ClassicHunger) ); } } } else { // Editing someone else if(!trustPlayers && !serverPlayer->connection->getNetworkPlayer()->IsHost()) { serverPlayer->setPlayerGamePrivilege(Player::ePlayerGamePrivilege_CannotMine,Player::getPlayerGamePrivilege(packet->m_playerPrivileges,Player::ePlayerGamePrivilege_CannotMine) ); serverPlayer->setPlayerGamePrivilege(Player::ePlayerGamePrivilege_CannotBuild,Player::getPlayerGamePrivilege(packet->m_playerPrivileges,Player::ePlayerGamePrivilege_CannotBuild) ); serverPlayer->setPlayerGamePrivilege(Player::ePlayerGamePrivilege_CannotAttackPlayers,Player::getPlayerGamePrivilege(packet->m_playerPrivileges,Player::ePlayerGamePrivilege_CannotAttackPlayers) ); serverPlayer->setPlayerGamePrivilege(Player::ePlayerGamePrivilege_CannotAttackAnimals,Player::getPlayerGamePrivilege(packet->m_playerPrivileges,Player::ePlayerGamePrivilege_CannotAttackAnimals) ); serverPlayer->setPlayerGamePrivilege(Player::ePlayerGamePrivilege_CanUseDoorsAndSwitches,Player::getPlayerGamePrivilege(packet->m_playerPrivileges,Player::ePlayerGamePrivilege_CanUseDoorsAndSwitches) ); serverPlayer->setPlayerGamePrivilege(Player::ePlayerGamePrivilege_CanUseContainers,Player::getPlayerGamePrivilege(packet->m_playerPrivileges,Player::ePlayerGamePrivilege_CanUseContainers) ); } if(networkPlayer->IsHost()) { if(cheats) { serverPlayer->setPlayerGamePrivilege(Player::ePlayerGamePrivilege_CanToggleInvisible,Player::getPlayerGamePrivilege(packet->m_playerPrivileges,Player::ePlayerGamePrivilege_CanToggleInvisible) ); serverPlayer->setPlayerGamePrivilege(Player::ePlayerGamePrivilege_CanToggleFly,Player::getPlayerGamePrivilege(packet->m_playerPrivileges,Player::ePlayerGamePrivilege_CanToggleFly) ); serverPlayer->setPlayerGamePrivilege(Player::ePlayerGamePrivilege_CanToggleClassicHunger,Player::getPlayerGamePrivilege(packet->m_playerPrivileges,Player::ePlayerGamePrivilege_CanToggleClassicHunger) ); serverPlayer->setPlayerGamePrivilege(Player::ePlayerGamePrivilege_CanTeleport,Player::getPlayerGamePrivilege(packet->m_playerPrivileges,Player::ePlayerGamePrivilege_CanTeleport) ); } serverPlayer->setPlayerGamePrivilege(Player::ePlayerGamePrivilege_Op,Player::getPlayerGamePrivilege(packet->m_playerPrivileges,Player::ePlayerGamePrivilege_Op) ); } } server->getPlayers()->broadcastAll( shared_ptr( new PlayerInfoPacket( serverPlayer ) ) ); } } } bool PlayerConnection::isServerPacketListener() { return true; } void PlayerConnection::handlePlayerAbilities(shared_ptr playerAbilitiesPacket) { player->abilities.flying = playerAbilitiesPacket->isFlying() && player->abilities.mayfly; } //void handleChatAutoComplete(ChatAutoCompletePacket packet) { // StringBuilder result = new StringBuilder(); // for (String candidate : server.getAutoCompletions(player, packet.getMessage())) { // if (result.length() > 0) result.append("\0"); // result.append(candidate); // } // player.connection.send(new ChatAutoCompletePacket(result.toString())); //} //void handleClientInformation(shared_ptr packet) //{ // player->updateOptions(packet); //} void PlayerConnection::handleCustomPayload(shared_ptr customPayloadPacket) { #if 0 if (CustomPayloadPacket.CUSTOM_BOOK_PACKET.equals(customPayloadPacket.identifier)) { ByteArrayInputStream bais(customPayloadPacket->data); DataInputStream input(&bais); shared_ptr sentItem = Packet::readItem(input); if (!WritingBookItem.makeSureTagIsValid(sentItem.getTag())) { throw new IOException("Invalid book tag!"); } // make sure the sent item is the currently carried item ItemInstance carried = player.inventory.getSelected(); if (sentItem != null && sentItem.id == Item.writingBook.id && sentItem.id == carried.id) { carried.setTag(sentItem.getTag()); } } else if (CustomPayloadPacket.CUSTOM_BOOK_SIGN_PACKET.equals(customPayloadPacket.identifier)) { DataInputStream input = new DataInputStream(new ByteArrayInputStream(customPayloadPacket.data)); ItemInstance sentItem = Packet.readItem(input); if (!WrittenBookItem.makeSureTagIsValid(sentItem.getTag())) { throw new IOException("Invalid book tag!"); } // make sure the sent item is the currently carried item ItemInstance carried = player.inventory.getSelected(); if (sentItem != null && sentItem.id == Item.writtenBook.id && carried.id == Item.writingBook.id) { carried.setTag(sentItem.getTag()); carried.id = Item.writtenBook.id; } } else #endif if (CustomPayloadPacket::TRADER_SELECTION_PACKET.compare(customPayloadPacket->identifier) == 0) { ByteArrayInputStream bais(customPayloadPacket->data); DataInputStream input(&bais); int selection = input.readInt(); AbstractContainerMenu *menu = player->containerMenu; if (dynamic_cast(menu)) { ((MerchantMenu *) menu)->setSelectionHint(selection); } } else if (CustomPayloadPacket::SET_ITEM_NAME_PACKET.compare(customPayloadPacket->identifier) == 0) { RepairMenu *menu = dynamic_cast( player->containerMenu); if (menu) { if (customPayloadPacket->data.data == NULL || customPayloadPacket->data.length < 1) { menu->setItemName(L""); } else { ByteArrayInputStream bais(customPayloadPacket->data); DataInputStream dis(&bais); wstring name = dis.readUTF(); if (name.length() <= 30) { menu->setItemName(name); } } } } } // 4J Added void PlayerConnection::handleDebugOptions(shared_ptr packet) { //Player player = dynamic_pointer_cast( player->shared_from_this() ); player->SetDebugOptions(packet->m_uiVal); } void PlayerConnection::handleCraftItem(shared_ptr packet) { int iRecipe = packet->recipe; if(iRecipe == -1) return; Recipy::INGREDIENTS_REQUIRED *pRecipeIngredientsRequired=Recipes::getInstance()->getRecipeIngredientsArray(); shared_ptr pTempItemInst=pRecipeIngredientsRequired[iRecipe].pRecipy->assemble(nullptr); if(app.DebugSettingsOn() && (player->GetDebugOptions()&(1L<onCraftedBy(player->level, dynamic_pointer_cast( player->shared_from_this() ), pTempItemInst->count ); if(player->inventory->add(pTempItemInst)==false ) { // no room in inventory, so throw it down player->drop(pTempItemInst); } } else { // TODO 4J Stu - Assume at the moment that the client can work this out for us... //if(pRecipeIngredientsRequired[iRecipe].bCanMake) //{ pTempItemInst->onCraftedBy(player->level, dynamic_pointer_cast( player->shared_from_this() ), pTempItemInst->count ); // and remove those resources from your inventory for(int i=0;i ingItemInst = nullptr; // do we need to remove a specific aux value? if(pRecipeIngredientsRequired[iRecipe].iIngAuxValA[i]!=Recipes::ANY_AUX_VALUE) { ingItemInst = player->inventory->getResourceItem( pRecipeIngredientsRequired[iRecipe].iIngIDA[i],pRecipeIngredientsRequired[iRecipe].iIngAuxValA[i] ); player->inventory->removeResource(pRecipeIngredientsRequired[iRecipe].iIngIDA[i],pRecipeIngredientsRequired[iRecipe].iIngAuxValA[i]); } else { ingItemInst = player->inventory->getResourceItem( pRecipeIngredientsRequired[iRecipe].iIngIDA[i] ); player->inventory->removeResource(pRecipeIngredientsRequired[iRecipe].iIngIDA[i]); } // 4J Stu - Fix for #13097 - Bug: Milk Buckets are removed when crafting Cake if (ingItemInst != NULL) { if (ingItemInst->getItem()->hasCraftingRemainingItem()) { // replace item with remaining result player->inventory->add( shared_ptr( new ItemInstance(ingItemInst->getItem()->getCraftingRemainingItem()) ) ); } } } } // 4J Stu - Fix for #13119 - We should add the item after we remove the ingredients if(player->inventory->add(pTempItemInst)==false ) { // no room in inventory, so throw it down player->drop(pTempItemInst); } if( pTempItemInst->id == Item::map_Id ) { // 4J Stu - Maps need to have their aux value update, so the client should always be assumed to be wrong // This is how the Java works, as the client also incorrectly predicts the auxvalue of the mapItem vector > items; for (unsigned int i = 0; i < player->containerMenu->slots->size(); i++) { items.push_back(player->containerMenu->slots->at(i)->getItem()); } player->refreshContainer(player->containerMenu, &items); } else { // Do same hack as PlayerConnection::handleContainerClick does - do our broadcast of changes just now, but with a hack so it just thinks it has sent // things but hasn't really. This will stop the client getting a message back confirming the current inventory items, which might then arrive // after another local change has been made on the client and be stale. player->ignoreSlotUpdateHack = true; player->containerMenu->broadcastChanges(); player->broadcastCarriedItem(); player->ignoreSlotUpdateHack = false; } } // handle achievements switch(pTempItemInst->id ) { case Tile::workBench_Id: player->awardStat(GenericStats::buildWorkbench(), GenericStats::param_buildWorkbench()); break; case Item::pickAxe_wood_Id: player->awardStat(GenericStats::buildPickaxe(), GenericStats::param_buildPickaxe()); break; case Tile::furnace_Id: player->awardStat(GenericStats::buildFurnace(), GenericStats::param_buildFurnace()); break; case Item::hoe_wood_Id: player->awardStat(GenericStats::buildHoe(), GenericStats::param_buildHoe()); break; case Item::bread_Id: player->awardStat(GenericStats::makeBread(), GenericStats::param_makeBread()); break; case Item::cake_Id: player->awardStat(GenericStats::bakeCake(), GenericStats::param_bakeCake()); break; case Item::pickAxe_stone_Id: player->awardStat(GenericStats::buildBetterPickaxe(), GenericStats::param_buildBetterPickaxe()); break; case Item::sword_wood_Id: player->awardStat(GenericStats::buildSword(), GenericStats::param_buildSword()); break; case Tile::dispenser_Id: player->awardStat(GenericStats::dispenseWithThis(), GenericStats::param_dispenseWithThis()); break; case Tile::enchantTable_Id: player->awardStat(GenericStats::enchantments(), GenericStats::param_enchantments()); break; case Tile::bookshelf_Id: player->awardStat(GenericStats::bookcase(), GenericStats::param_bookcase()); break; } //} // ELSE The server thinks the client was wrong... } void PlayerConnection::handleTradeItem(shared_ptr packet) { if (player->containerMenu->containerId == packet->containerId) { MerchantMenu *menu = (MerchantMenu *)player->containerMenu; MerchantRecipeList *offers = menu->getMerchant()->getOffers(player); if(offers) { int selectedShopItem = packet->offer; if( selectedShopItem < offers->size() ) { MerchantRecipe *activeRecipe = offers->at(selectedShopItem); if(!activeRecipe->isDeprecated()) { // Do we have the ingredients? shared_ptr buyAItem = activeRecipe->getBuyAItem(); shared_ptr buyBItem = activeRecipe->getBuyBItem(); int buyAMatches = player->inventory->countMatches(buyAItem); int buyBMatches = player->inventory->countMatches(buyBItem); if( (buyAItem != NULL && buyAMatches >= buyAItem->count) && (buyBItem == NULL || buyBMatches >= buyBItem->count) ) { menu->getMerchant()->notifyTrade(activeRecipe); // Remove the items we are purchasing with player->inventory->removeResources(buyAItem); player->inventory->removeResources(buyBItem); // Add the item we have purchased shared_ptr result = activeRecipe->getSellItem()->copy(); // 4J JEV - Award itemsBought stat. player->awardStat( GenericStats::itemsBought(result->getItem()->id), GenericStats::param_itemsBought( result->getItem()->id, result->getAuxValue(), result->GetCount() ) ); if (!player->inventory->add(result)) { player->drop(result); } } } } } } } INetworkPlayer *PlayerConnection::getNetworkPlayer() { if( connection != NULL && connection->getSocket() != NULL) return connection->getSocket()->getPlayer(); else return NULL; } bool PlayerConnection::isLocal() { if( connection->getSocket() == NULL ) { return false; } else { bool isLocal = connection->getSocket()->isLocal(); return connection->getSocket()->isLocal(); } } bool PlayerConnection::isGuest() { if( connection->getSocket() == NULL ) { return false; } else { INetworkPlayer *networkPlayer = connection->getSocket()->getPlayer(); bool isGuest = false; if(networkPlayer != NULL) { isGuest = networkPlayer->IsGuest() == TRUE; } return isGuest; } }