diff --git a/Minecraft.Client/Network/ClientConnection.cpp b/Minecraft.Client/Network/ClientConnection.cpp index 65eeedc0f..ab03b3403 100644 --- a/Minecraft.Client/Network/ClientConnection.cpp +++ b/Minecraft.Client/Network/ClientConnection.cpp @@ -13,7 +13,9 @@ #include "../../Minecraft.World/Headers/net.minecraft.world.level.chunk.h" #include "../../Minecraft.World/Headers/net.minecraft.stats.h" #include "../../Minecraft.World/Headers/net.minecraft.world.entity.h" +#include "../../Minecraft.World/Headers/net.minecraft.world.entity.ai.attributes.h" #include "../../Minecraft.World/Headers/net.minecraft.world.entity.player.h" +#include "../../Minecraft.World/Headers/net.minecraft.world.entity.animal.h" #include "../../Minecraft.World/Headers/net.minecraft.world.entity.npc.h" #include "../../Minecraft.World/Headers/net.minecraft.world.entity.item.h" #include "../../Minecraft.World/Headers/net.minecraft.world.entity.projectile.h" @@ -41,6 +43,7 @@ #include "../MinecraftServer.h" #include "../ClientConstants.h" #include "../../Minecraft.World/Util/SoundTypes.h" +#include "../../Minecraft.World/Util/BasicTypeContainers.h" #include "../Textures/Packs/TexturePackRepository.h" #ifdef _XBOX #include "../Platform/Common/XUI/XUI_Scene_Trading.h" @@ -123,6 +126,8 @@ ClientConnection::ClientConnection(Minecraft* minecraft, Socket* socket, // the socket // delete socket; } + + deferredEntityLinkPackets = std::vector(); } ClientConnection::~ClientConnection() { @@ -422,20 +427,9 @@ void ClientConnection::handleAddEntity( // 4J-PB - replacing this massive if nest with switch switch (packet->type) { - case AddEntityPacket::MINECART_RIDEABLE: - e = std::shared_ptr( - new Minecart(level, x, y, z, Minecart::RIDEABLE)); + case AddEntityPacket::MINECART: + e = Minecart::createMinecart(level, x, y, z, packet->data); break; - case AddEntityPacket::MINECART_CHEST: - e = std::shared_ptr( - new Minecart(level, x, y, z, Minecart::CHEST)); - break; - - case AddEntityPacket::MINECART_FURNACE: - e = std::shared_ptr( - new Minecart(level, x, y, z, Minecart::FURNACE)); - break; - case AddEntityPacket::FISH_HOOK: { // 4J Stu - Brought forward from 1.4 to be able to drop XP from // fishing @@ -453,9 +447,10 @@ void ClientConnection::handleAddEntity( } } } - std::shared_ptr player = - std::dynamic_pointer_cast(owner); - if (player != NULL) { + + if (owner->instanceof(eTYPE_PLAYER)) { + std::shared_ptr player = + std::dynamic_pointer_cast(owner); std::shared_ptr hook = std::shared_ptr( new FishingHook(level, x, y, z, player)); @@ -492,8 +487,8 @@ void ClientConnection::handleAddEntity( break; case AddEntityPacket::FIREBALL: e = std::shared_ptr( - new Fireball(level, x, y, z, packet->xa / 8000.0, - packet->ya / 8000.0, packet->za / 8000.0)); + new LargeFireball(level, x, y, z, packet->xa / 8000.0, + packet->ya / 8000.0, packet->za / 8000.0)); packet->data = 0; break; case AddEntityPacket::SMALL_FIREBALL: @@ -524,7 +519,7 @@ void ClientConnection::handleAddEntity( e = std::shared_ptr(new Boat(level, x, y, z)); break; case AddEntityPacket::PRIMED_TNT: - e = std::shared_ptr(new PrimedTnt(level, x, y, z)); + e = std::shared_ptr(new PrimedTnt(level, x, y, z, nullptr)); break; case AddEntityPacket::ENDER_CRYSTAL: e = std::shared_ptr(new EnderCrystal(level, x, y, z)); @@ -537,7 +532,28 @@ void ClientConnection::handleAddEntity( level, x, y, z, packet->data & 0xFFFF, packet->data >> 16)); packet->data = 0; break; + case AddEntityPacket::WITHER_SKULL: + e = std::shared_ptr( + new WitherSkull(level, x, y, z, packet->xa / 8000.0, + packet->ya / 8000.0, packet->za / 8000.0)); + packet->data = 0; + break; + case AddEntityPacket::FIREWORKS: + e = std::shared_ptr( + new FireworksRocketEntity(level, x, y, z, nullptr)); + break; + case AddEntityPacket::LEASH_KNOT: + e = std::shared_ptr( + new LeashFenceKnotEntity(level, (int)x, (int)y, (int)z)); + packet->data = 0; + break; +#ifndef _FINAL_BUILD + default: + // Not a known entity (?) + assert(0); +#endif } + /* if (packet->type == AddEntityPacket::MINECART_RIDEABLE) e = std::shared_ptr( new Minecart(level, x, y, z, Minecart::RIDEABLE) ); if (packet->type == AddEntityPacket::MINECART_CHEST) e = @@ -588,38 +604,38 @@ void ClientConnection::handleAddEntity( EyeOfEnderSignal(level, x, y, z) ); if (packet->type == AddEntityPacket::FIREBALL) { - e = std::shared_ptr( new Fireball(level, x, y, z, packet->xa - / 8000.0, packet->ya / 8000.0, packet->za / 8000.0) ); packet->data = 0; - } - if (packet->type == AddEntityPacket::SMALL_FIREBALL) - { - e = std::shared_ptr( new SmallFireball(level, x, y, - z, packet->xa / 8000.0, packet->ya / 8000.0, packet->za / 8000.0) ); + e = shared_ptr( new Fireball(level, x, y, z, + packet->xa / 8000.0, packet->ya / 8000.0, packet->za / 8000.0) ); packet->data = 0; } - if (packet->type == AddEntityPacket::EGG) e = std::shared_ptr( - new ThrownEgg(level, x, y, z) ); if (packet->type == + if (packet->type == AddEntityPacket::SMALL_FIREBALL) + { + e = shared_ptr( new SmallFireball(level, x, y, z, + packet->xa / 8000.0, packet->ya / 8000.0, packet->za / 8000.0) ); + packet->data = 0; + } + if (packet->type == AddEntityPacket::EGG) e = shared_ptr( new + ThrownEgg(level, x, y, z) ); if (packet->type == AddEntityPacket::THROWN_POTION) { - e = std::shared_ptr( new ThrownPotion(level, x, y, z, + e = shared_ptr( new ThrownPotion(level, x, y, z, packet->data) ); packet->data = 0; } if (packet->type == AddEntityPacket::THROWN_EXPBOTTLE) { - e = std::shared_ptr( new ThrownExpBottle(level, x, y, - z) ); packet->data = 0; + e = shared_ptr( new ThrownExpBottle(level, x, y, z) + ); packet->data = 0; } - if (packet->type == AddEntityPacket::BOAT) e = std::shared_ptr( + if (packet->type == AddEntityPacket::BOAT) e = shared_ptr( new Boat(level, x, y, z) ); if (packet->type == - AddEntityPacket::PRIMED_TNT) e = std::shared_ptr( new - PrimedTnt(level, x, y, z) ); if (packet->type == - AddEntityPacket::ENDER_CRYSTAL) e = std::shared_ptr( new - EnderCrystal(level, x, y, z) ); if (packet->type == - AddEntityPacket::FALLING_SAND) e = std::shared_ptr( new + AddEntityPacket::PRIMED_TNT) e = shared_ptr( new PrimedTnt(level, + x, y, z) ); if (packet->type == AddEntityPacket::ENDER_CRYSTAL) e = + shared_ptr( new EnderCrystal(level, x, y, z) ); if (packet->type + == AddEntityPacket::FALLING_SAND) e = shared_ptr( new FallingTile(level, x, y, z, Tile::sand->id) ); if (packet->type == - AddEntityPacket::FALLING_GRAVEL) e = std::shared_ptr( new + AddEntityPacket::FALLING_GRAVEL) e = shared_ptr( new FallingTile(level, x, y, z, Tile::gravel->id) ); if (packet->type == - AddEntityPacket::FALLING_EGG) e = std::shared_ptr( new + AddEntityPacket::FALLING_EGG) e = shared_ptr( new FallingTile(level, x, y, z, Tile::dragonEgg_Id) ); */ @@ -652,11 +668,17 @@ void ClientConnection::handleAddEntity( } } - // Note - not doing this move for frame, as the ctor for these objects - // does some adjustments on the position based on direction to move the - // object out slightly from what it is attached to, and this just - // overwrites it - if (packet->type != AddEntityPacket::ITEM_FRAME) { + if (packet->type == AddEntityPacket::LEASH_KNOT) { + // 4J: "Move" leash knot to it's current position, this sets old + // position (like frame, leash has adjusted position) + e->absMoveTo(e->x, e->y, e->z, yRot, xRot); + } else if (packet->type == AddEntityPacket::ITEM_FRAME) { + // Not doing this move for frame, as the ctor for these objects does + // some adjustments on the position based on direction to move the + // object out slightly from what it is attached to, and this just + // overwrites it + } else { + // For everything else, set position e->absMoveTo(x, y, z, yRot, xRot); } e->entityId = packet->id; @@ -681,15 +703,18 @@ void ClientConnection::handleAddEntity( } } - if (std::dynamic_pointer_cast(owner) != NULL) { + if (owner != NULL && owner->instanceof(eTYPE_LIVINGENTITY)) { std::dynamic_pointer_cast(e)->owner = - std::dynamic_pointer_cast(owner); + std::dynamic_pointer_cast(owner); } } e->lerpMotion(packet->xa / 8000.0, packet->ya / 8000.0, packet->za / 8000.0); } + + // 4J: Check our deferred entity link packets + checkDeferredEntityLinkPackets(e->entityId); } } @@ -789,11 +814,11 @@ void ClientConnection::handleAddPlayer( INetworkPlayer* networkPlayer = g_NetworkManager.GetPlayerByXuid(player->getXuid()); if (networkPlayer != NULL) - player->displayName = networkPlayer->GetDisplayName(); + player->m_displayName = networkPlayer->GetDisplayName(); #else // On all other platforms display name is just gamertag so don't check with // the network manager - player->displayName = player->name; + player->m_displayName = player->name; #endif // printf("\t\t\t\t%d: Add player\n",packet->id,packet->yRot); @@ -890,6 +915,16 @@ void ClientConnection::handleTeleportEntity( e->lerpTo(x, y, z, yRot, xRot, 3); } +void ClientConnection::handleSetCarriedItem( + std::shared_ptr packet) { + if (packet->slot >= 0 && packet->slot < Inventory::getSelectionSize()) { + Minecraft::GetInstance() + ->localplayers[m_userIndex] + .get() + ->inventory->selected = packet->slot; + } +} + void ClientConnection::handleMoveEntity( std::shared_ptr packet) { std::shared_ptr e = getEntity(packet->id); @@ -1116,23 +1151,31 @@ void ClientConnection::handleBlockRegionUpdate( int y1 = packet->y + packet->ys; if (packet->bIsFullChunk) { y1 = Level::maxBuildHeight; - if (packet->buffer.length > 0) + if (packet->buffer.length > 0) { + PIXBeginNamedEvent(0, "Reordering to XZY"); LevelChunk::reorderBlocksAndDataToXZY(packet->y, packet->xs, packet->ys, packet->zs, &packet->buffer); + PIXEndNamedEvent(); + } } + PIXBeginNamedEvent(0, "Clear rest region"); dimensionLevel->clearResetRegion(packet->x, packet->y, packet->z, packet->x + packet->xs - 1, y1 - 1, packet->z + packet->zs - 1); + PIXEndNamedEvent(); + PIXBeginNamedEvent(0, "setBlocksAndData"); // Only full chunks send lighting information now - added flag to end of // this call dimensionLevel->setBlocksAndData(packet->x, packet->y, packet->z, packet->xs, packet->ys, packet->zs, packet->buffer, packet->bIsFullChunk); + PIXEndNamedEvent(); // OutputDebugString("END BRU\n"); + PIXBeginNamedEvent(0, "removeUnusedTileEntitiesInRegion"); // 4J - remove any tite entities in this region which are associated // with a tile that is now no longer a tile entity. Without doing this // we end up with stray tile entities kicking round, which leads to a @@ -1141,14 +1184,17 @@ void ClientConnection::handleBlockRegionUpdate( dimensionLevel->removeUnusedTileEntitiesInRegion( packet->x, packet->y, packet->z, packet->x + packet->xs, y1, packet->z + packet->zs); + PIXEndNamedEvent(); // If this is a full packet for a chunk, make sure that the cache now // considers that it has data for this chunk - this is used to determine // whether to bother rendering mobs or not, so we don't have them in // crazy positions before the data is there if (packet->bIsFullChunk) { + PIXBeginNamedEvent(0, "dateReceivedForChunk"); dimensionLevel->dataReceivedForChunk(packet->x >> 4, packet->z >> 4); + PIXEndNamedEvent(); } PIXEndNamedEvent(); } @@ -1271,10 +1317,9 @@ void ClientConnection::onDisconnect(DisconnectPacket::eDisconnectReason reason, !MinecraftServer::saveOnExitAnswered()) { unsigned int uiIDA[1]; uiIDA[0] = IDS_CONFIRM_OK; - ui.RequestMessageBox(IDS_EXITING_GAME, IDS_GENERIC_ERROR, uiIDA, 1, - ProfileManager.GetPrimaryPad(), - &ClientConnection::HostDisconnectReturned, NULL, - app.GetStringTable()); + ui.RequestErrorMessage(IDS_EXITING_GAME, IDS_GENERIC_ERROR, uiIDA, 1, + ProfileManager.GetPrimaryPad(), + &ClientConnection::HostDisconnectReturned, NULL); } else { app.SetAction(m_userIndex, eAppAction_ExitWorld, (void*)TRUE); } @@ -1298,8 +1343,8 @@ void ClientConnection::send(std::shared_ptr packet) { void ClientConnection::handleTakeItemEntity( std::shared_ptr packet) { std::shared_ptr from = getEntity(packet->itemId); - std::shared_ptr to = - std::dynamic_pointer_cast(getEntity(packet->playerId)); + std::shared_ptr to = + std::dynamic_pointer_cast(getEntity(packet->playerId)); // 4J - the original game could assume that if getEntity didn't find the // player, it must be the local player. We need to search all local players @@ -1386,6 +1431,10 @@ void ClientConnection::handleChat(std::shared_ptr packet) { int iPos; bool displayOnGui = true; + bool replacePlayer = false; + bool replaceEntitySource = false; + bool replaceItem = false; + std::wstring playerDisplayName = L""; std::wstring sourceDisplayName = L""; @@ -1437,132 +1486,214 @@ void ClientConnection::handleChat(std::shared_ptr packet) { break; case ChatPacket::e_ChatDeathInFire: message = app.GetString(IDS_DEATH_INFIRE); - message = replaceAll(message, L"{*PLAYER*}", playerDisplayName); + replacePlayer = true; break; case ChatPacket::e_ChatDeathOnFire: message = app.GetString(IDS_DEATH_ONFIRE); - message = replaceAll(message, L"{*PLAYER*}", playerDisplayName); + replacePlayer = true; break; case ChatPacket::e_ChatDeathLava: message = app.GetString(IDS_DEATH_LAVA); - message = replaceAll(message, L"{*PLAYER*}", playerDisplayName); + replacePlayer = true; break; case ChatPacket::e_ChatDeathInWall: message = app.GetString(IDS_DEATH_INWALL); - message = replaceAll(message, L"{*PLAYER*}", playerDisplayName); + replacePlayer = true; break; case ChatPacket::e_ChatDeathDrown: message = app.GetString(IDS_DEATH_DROWN); - message = replaceAll(message, L"{*PLAYER*}", playerDisplayName); + replacePlayer = true; break; case ChatPacket::e_ChatDeathStarve: message = app.GetString(IDS_DEATH_STARVE); - message = replaceAll(message, L"{*PLAYER*}", playerDisplayName); + replacePlayer = true; break; case ChatPacket::e_ChatDeathCactus: message = app.GetString(IDS_DEATH_CACTUS); - message = replaceAll(message, L"{*PLAYER*}", playerDisplayName); + replacePlayer = true; break; case ChatPacket::e_ChatDeathFall: message = app.GetString(IDS_DEATH_FALL); - message = replaceAll(message, L"{*PLAYER*}", playerDisplayName); + replacePlayer = true; break; case ChatPacket::e_ChatDeathOutOfWorld: message = app.GetString(IDS_DEATH_OUTOFWORLD); - message = replaceAll(message, L"{*PLAYER*}", playerDisplayName); + replacePlayer = true; break; case ChatPacket::e_ChatDeathGeneric: message = app.GetString(IDS_DEATH_GENERIC); - message = replaceAll(message, L"{*PLAYER*}", playerDisplayName); + replacePlayer = true; break; case ChatPacket::e_ChatDeathExplosion: message = app.GetString(IDS_DEATH_EXPLOSION); - message = replaceAll(message, L"{*PLAYER*}", playerDisplayName); + replacePlayer = true; break; case ChatPacket::e_ChatDeathMagic: message = app.GetString(IDS_DEATH_MAGIC); - message = replaceAll(message, L"{*PLAYER*}", playerDisplayName); + replacePlayer = true; break; case ChatPacket::e_ChatDeathAnvil: message = app.GetString(IDS_DEATH_FALLING_ANVIL); - message = replaceAll(message, L"{*PLAYER*}", playerDisplayName); + replacePlayer = true; break; case ChatPacket::e_ChatDeathFallingBlock: message = app.GetString(IDS_DEATH_FALLING_TILE); - message = replaceAll(message, L"{*PLAYER*}", playerDisplayName); + replacePlayer = true; break; case ChatPacket::e_ChatDeathDragonBreath: message = app.GetString(IDS_DEATH_DRAGON_BREATH); - message = replaceAll(message, L"{*PLAYER*}", playerDisplayName); + replacePlayer = true; break; case ChatPacket::e_ChatDeathMob: message = app.GetString(IDS_DEATH_MOB); - message = replaceAll(message, L"{*PLAYER*}", playerDisplayName); - message = replaceAll( - message, L"{*SOURCE*}", - app.getEntityName((eINSTANCEOF)packet->m_intArgs[0])); + replacePlayer = true; + replaceEntitySource = true; break; case ChatPacket::e_ChatDeathPlayer: message = app.GetString(IDS_DEATH_PLAYER); - message = replaceAll(message, L"{*PLAYER*}", playerDisplayName); - message = replaceAll(message, L"{*SOURCE*}", sourceDisplayName); + replacePlayer = true; + replaceEntitySource = true; break; case ChatPacket::e_ChatDeathArrow: message = app.GetString(IDS_DEATH_ARROW); - message = replaceAll(message, L"{*PLAYER*}", playerDisplayName); - if (packet->m_intArgs[0] == eTYPE_SERVERPLAYER) { - message = replaceAll(message, L"{*SOURCE*}", sourceDisplayName); - } else { - message = replaceAll( - message, L"{*SOURCE*}", - app.getEntityName((eINSTANCEOF)packet->m_intArgs[0])); - } + replacePlayer = true; + replaceEntitySource = true; break; case ChatPacket::e_ChatDeathFireball: message = app.GetString(IDS_DEATH_FIREBALL); - message = replaceAll(message, L"{*PLAYER*}", playerDisplayName); - if (packet->m_intArgs[0] == eTYPE_SERVERPLAYER) { - message = - replaceAll(message, L"{*SOURCE*}", packet->m_stringArgs[1]); - } else { - message = replaceAll( - message, L"{*SOURCE*}", - app.getEntityName((eINSTANCEOF)packet->m_intArgs[0])); - } + replacePlayer = true; + replaceEntitySource = true; break; case ChatPacket::e_ChatDeathThrown: message = app.GetString(IDS_DEATH_THROWN); - message = replaceAll(message, L"{*PLAYER*}", playerDisplayName); - if (packet->m_intArgs[0] == eTYPE_SERVERPLAYER) { - message = replaceAll(message, L"{*SOURCE*}", sourceDisplayName); - } else { - message = replaceAll( - message, L"{*SOURCE*}", - app.getEntityName((eINSTANCEOF)packet->m_intArgs[0])); - } + replacePlayer = true; + replaceEntitySource = true; break; case ChatPacket::e_ChatDeathIndirectMagic: message = app.GetString(IDS_DEATH_INDIRECT_MAGIC); - message = replaceAll(message, L"{*PLAYER*}", playerDisplayName); - if (packet->m_intArgs[0] == eTYPE_SERVERPLAYER) { - message = replaceAll(message, L"{*SOURCE*}", sourceDisplayName); - } else { - message = replaceAll( - message, L"{*SOURCE*}", - app.getEntityName((eINSTANCEOF)packet->m_intArgs[0])); - } + replacePlayer = true; + replaceEntitySource = true; break; case ChatPacket::e_ChatDeathThorns: message = app.GetString(IDS_DEATH_THORNS); - message = replaceAll(message, L"{*PLAYER*}", playerDisplayName); - if (packet->m_intArgs[0] == eTYPE_SERVERPLAYER) { - message = replaceAll(message, L"{*SOURCE*}", sourceDisplayName); - } else { - message = replaceAll( - message, L"{*SOURCE*}", - app.getEntityName((eINSTANCEOF)packet->m_intArgs[0])); - } + replacePlayer = true; + replaceEntitySource = true; break; + + case ChatPacket::e_ChatDeathFellAccidentLadder: + message = app.GetString(IDS_DEATH_FELL_ACCIDENT_LADDER); + replacePlayer = true; + break; + case ChatPacket::e_ChatDeathFellAccidentVines: + message = app.GetString(IDS_DEATH_FELL_ACCIDENT_VINES); + replacePlayer = true; + break; + case ChatPacket::e_ChatDeathFellAccidentWater: + message = app.GetString(IDS_DEATH_FELL_ACCIDENT_WATER); + replacePlayer = true; + break; + case ChatPacket::e_ChatDeathFellAccidentGeneric: + message = app.GetString(IDS_DEATH_FELL_ACCIDENT_GENERIC); + replacePlayer = true; + break; + case ChatPacket::e_ChatDeathFellKiller: + // message=app.GetString(IDS_DEATH_FELL_KILLER); + // replacePlayer = true; + // replaceEntitySource = true; + + // 4J Stu - The correct string for here, IDS_DEATH_FELL_KILLER is + // incorrect. We can't change localisation, so use a different + // string for now + message = app.GetString(IDS_DEATH_FALL); + replacePlayer = true; + break; + case ChatPacket::e_ChatDeathFellAssist: + message = app.GetString(IDS_DEATH_FELL_ASSIST); + replacePlayer = true; + replaceEntitySource = true; + break; + case ChatPacket::e_ChatDeathFellAssistItem: + message = app.GetString(IDS_DEATH_FELL_ASSIST_ITEM); + replacePlayer = true; + replaceEntitySource = true; + replaceItem = true; + break; + case ChatPacket::e_ChatDeathFellFinish: + message = app.GetString(IDS_DEATH_FELL_FINISH); + replacePlayer = true; + replaceEntitySource = true; + break; + case ChatPacket::e_ChatDeathFellFinishItem: + message = app.GetString(IDS_DEATH_FELL_FINISH_ITEM); + replacePlayer = true; + replaceEntitySource = true; + replaceItem = true; + break; + case ChatPacket::e_ChatDeathInFirePlayer: + message = app.GetString(IDS_DEATH_INFIRE_PLAYER); + replacePlayer = true; + replaceEntitySource = true; + break; + case ChatPacket::e_ChatDeathOnFirePlayer: + message = app.GetString(IDS_DEATH_ONFIRE_PLAYER); + replacePlayer = true; + replaceEntitySource = true; + break; + case ChatPacket::e_ChatDeathLavaPlayer: + message = app.GetString(IDS_DEATH_LAVA_PLAYER); + replacePlayer = true; + replaceEntitySource = true; + break; + case ChatPacket::e_ChatDeathDrownPlayer: + message = app.GetString(IDS_DEATH_DROWN_PLAYER); + replacePlayer = true; + replaceEntitySource = true; + break; + case ChatPacket::e_ChatDeathCactusPlayer: + message = app.GetString(IDS_DEATH_CACTUS_PLAYER); + replacePlayer = true; + replaceEntitySource = true; + break; + case ChatPacket::e_ChatDeathExplosionPlayer: + message = app.GetString(IDS_DEATH_EXPLOSION_PLAYER); + replacePlayer = true; + replaceEntitySource = true; + break; + case ChatPacket::e_ChatDeathWither: + message = app.GetString(IDS_DEATH_WITHER); + replacePlayer = true; + break; + case ChatPacket::e_ChatDeathPlayerItem: + message = app.GetString(IDS_DEATH_PLAYER_ITEM); + replacePlayer = true; + replaceEntitySource = true; + replaceItem = true; + break; + case ChatPacket::e_ChatDeathArrowItem: + message = app.GetString(IDS_DEATH_ARROW_ITEM); + replacePlayer = true; + replaceEntitySource = true; + replaceItem = true; + break; + case ChatPacket::e_ChatDeathFireballItem: + message = app.GetString(IDS_DEATH_FIREBALL_ITEM); + replacePlayer = true; + replaceEntitySource = true; + replaceItem = true; + break; + case ChatPacket::e_ChatDeathThrownItem: + message = app.GetString(IDS_DEATH_THROWN_ITEM); + replacePlayer = true; + replaceEntitySource = true; + replaceItem = true; + break; + case ChatPacket::e_ChatDeathIndirectMagicItem: + message = app.GetString(IDS_DEATH_INDIRECT_MAGIC_ITEM); + replacePlayer = true; + replaceEntitySource = true; + replaceItem = true; + break; + case ChatPacket::e_ChatPlayerEnteredEnd: message = app.GetString(IDS_PLAYER_ENTERED_END); iPos = message.find(L"%s"); @@ -1596,6 +1727,9 @@ void ClientConnection::handleChat(std::shared_ptr packet) { case ChatPacket::e_ChatPlayerMaxWolves: message = app.GetString(IDS_MAX_WOLVES_SPAWNED); break; + case ChatPacket::e_ChatPlayerMaxBats: + message = app.GetString(IDS_MAX_BATS_SPAWNED); + break; // Breeding case ChatPacket::e_ChatPlayerMaxBredPigsSheepCows: @@ -1633,10 +1767,10 @@ void ClientConnection::handleChat(std::shared_ptr packet) { case ChatPacket::e_ChatCommandTeleportSuccess: message = app.GetString(IDS_COMMAND_TELEPORT_SUCCESS); - message = replaceAll(message, L"{*PLAYER*}", playerDisplayName); + replacePlayer = true; if (packet->m_intArgs[0] == eTYPE_SERVERPLAYER) { - message = replaceAll(message, L"{*DESTINATION*}", - packet->m_stringArgs[1]); + message = + replaceAll(message, L"{*DESTINATION*}", sourceDisplayName); } else { message = replaceAll( message, L"{*DESTINATION*}", @@ -1645,11 +1779,11 @@ void ClientConnection::handleChat(std::shared_ptr packet) { break; case ChatPacket::e_ChatCommandTeleportMe: message = app.GetString(IDS_COMMAND_TELEPORT_ME); - message = replaceAll(message, L"{*PLAYER*}", playerDisplayName); + replacePlayer = true; break; case ChatPacket::e_ChatCommandTeleportToMe: message = app.GetString(IDS_COMMAND_TELEPORT_TO_ME); - message = replaceAll(message, L"{*PLAYER*}", playerDisplayName); + replacePlayer = true; break; default: @@ -1657,10 +1791,37 @@ void ClientConnection::handleChat(std::shared_ptr packet) { break; } + if (replacePlayer) { + message = replaceAll(message, L"{*PLAYER*}", playerDisplayName); + } + + if (replaceEntitySource) { + if (packet->m_intArgs[0] == eTYPE_SERVERPLAYER) { + message = replaceAll(message, L"{*SOURCE*}", sourceDisplayName); + } else { + std::wstring entityName; + + // Check for a custom mob name + if (packet->m_stringArgs.size() >= 2 && + !packet->m_stringArgs[1].empty()) { + entityName = packet->m_stringArgs[1]; + } else { + entityName = + app.getEntityName((eINSTANCEOF)packet->m_intArgs[0]); + } + + message = replaceAll(message, L"{*SOURCE*}", entityName); + } + } + + if (replaceItem) { + message = replaceAll(message, L"{*ITEM*}", packet->m_stringArgs[2]); + } + // flag that a message is a death message bool bIsDeathMessage = (packet->m_messageType >= ChatPacket::e_ChatDeathInFire) && - (packet->m_messageType <= ChatPacket::e_ChatDeathDragonBreath); + (packet->m_messageType <= ChatPacket::e_ChatDeathIndirectMagicItem); if (displayOnGui) minecraft->gui->addMessage(message, m_userIndex, bIsDeathMessage); @@ -1670,13 +1831,14 @@ void ClientConnection::handleAnimate(std::shared_ptr packet) { std::shared_ptr e = getEntity(packet->id); if (e == NULL) return; if (packet->action == AnimatePacket::SWING) { - std::shared_ptr player = std::dynamic_pointer_cast(e); - if (player != NULL) player->swing(); + if (e->instanceof(eTYPE_LIVINGENTITY)) + std::dynamic_pointer_cast(e)->swing(); } else if (packet->action == AnimatePacket::HURT) { e->animateHurt(); } else if (packet->action == AnimatePacket::WAKE_UP) { - std::shared_ptr player = std::dynamic_pointer_cast(e); - if (player != NULL) player->stopSleepInBed(false, false, false); + if (e->instanceof(eTYPE_PLAYER)) + std::dynamic_pointer_cast(e)->stopSleepInBed(false, false, + false); } else if (packet->action == AnimatePacket::RESPAWN) { } else if (packet->action == AnimatePacket::CRITICAL_HIT) { std::shared_ptr critParticle = @@ -1690,8 +1852,8 @@ void ClientConnection::handleAnimate(std::shared_ptr packet) { new CritParticle(minecraft->level, e, eParticleType_magicCrit)); critParticle->CritParticlePostConstructor(); minecraft->particleEngine->add(critParticle); - } else if (packet->action == AnimatePacket::EAT && - std::dynamic_pointer_cast(e) != NULL) { + } else if ((packet->action == AnimatePacket::EAT) && + e->instanceof(eTYPE_REMOTEPLAYER)) { } } @@ -2009,11 +2171,12 @@ void ClientConnection::handlePreLogin(std::shared_ptr packet) { // Check this friend against each player, if we find them we // have at least one friend for (int j = 0; j < g_NetworkManager.GetPlayerCount(); j++) { - Platform::String ^ xboxUserId = ref new Platform::String( - g_NetworkManager.GetPlayerByIndex(j) - ->GetUID() - .toString() - .data()); + Platform::String ^ xboxUserId = + ref new Platform::String( + g_NetworkManager.GetPlayerByIndex(j) + ->GetUID() + .toString() + .data()); if (friendsXuid == xboxUserId) { isAtLeastOneFriend = true; break; @@ -2101,14 +2264,14 @@ void ClientConnection::handlePreLogin(std::shared_ptr packet) { unsigned int uiIDA[1]; uiIDA[0] = IDS_CONFIRM_OK; if (!isFriendsWithHost) - ui.RequestMessageBox( - IDS_CANTJOIN_TITLE, IDS_NOTALLOWED_FRIENDSOFFRIENDS, uiIDA, - 1, m_userIndex, NULL, NULL, app.GetStringTable()); + ui.RequestErrorMessage(IDS_CANTJOIN_TITLE, + IDS_NOTALLOWED_FRIENDSOFFRIENDS, uiIDA, + 1, m_userIndex); else - ui.RequestMessageBox( + ui.RequestErrorMessage( IDS_CANTJOIN_TITLE, IDS_NO_USER_CREATED_CONTENT_PRIVILEGE_SINGLE_LOCAL, uiIDA, - 1, m_userIndex, NULL, NULL, app.GetStringTable()); + 1, m_userIndex); app.SetDisconnectReason(reason); @@ -2144,7 +2307,9 @@ void ClientConnection::handlePreLogin(std::shared_ptr packet) { packet->m_texturePackId); // 4J-PB - we need to upsell the texture pack to the player - // app.SetAction(m_userIndex,eAppAction_TexturePackRequired); +#if defined __PS3__ || defined __ORBIS__ || defined __PSVITA__ + app.SetAction(m_userIndex, eAppAction_TexturePackRequired); +#endif // Let the player go into the game, and we'll check that they // are using the right texture pack when in } @@ -2251,8 +2416,8 @@ void ClientConnection::handleAddMob(std::shared_ptr packet) { float yRot = packet->yRot * 360 / 256.0f; float xRot = packet->xRot * 360 / 256.0f; - std::shared_ptr mob = - std::dynamic_pointer_cast(EntityIO::newById(packet->type, level)); + std::shared_ptr mob = std::dynamic_pointer_cast( + EntityIO::newById(packet->type, level)); mob->xp = packet->x; mob->yp = packet->y; mob->zp = packet->z; @@ -2297,7 +2462,8 @@ void ClientConnection::handleAddMob(std::shared_ptr packet) { } void ClientConnection::handleSetTime(std::shared_ptr packet) { - minecraft->level->setTime(packet->time); + minecraft->level->setGameTime(packet->gameTime); + minecraft->level->setDayTime(packet->dayTime); } void ClientConnection::handleSetSpawn( @@ -2305,28 +2471,66 @@ void ClientConnection::handleSetSpawn( // minecraft->player->setRespawnPosition(new Pos(packet->x, packet->y, // packet->z)); minecraft->localplayers[m_userIndex]->setRespawnPosition( - new Pos(packet->x, packet->y, packet->z)); + new Pos(packet->x, packet->y, packet->z), true); minecraft->level->getLevelData()->setSpawn(packet->x, packet->y, packet->z); } -void ClientConnection::handleRidePacket( - std::shared_ptr packet) { - std::shared_ptr rider = getEntity(packet->riderId); - std::shared_ptr ridden = getEntity(packet->riddenId); +void ClientConnection::handleEntityLinkPacket( + std::shared_ptr packet) { + std::shared_ptr sourceEntity = getEntity(packet->sourceId); + std::shared_ptr destEntity = getEntity(packet->destId); - std::shared_ptr boat = std::dynamic_pointer_cast(ridden); - // if (packet->riderId == minecraft->player->entityId) rider = - // minecraft->player; - if (packet->riderId == minecraft->localplayers[m_userIndex]->entityId) { - rider = minecraft->localplayers[m_userIndex]; + // 4J: If the destination entity couldn't be found, defer handling of this + // packet This was added to support leashing (the entity link packet is sent + // before the add entity packet) + if (destEntity == NULL && packet->destId >= 0) { + // We don't handle missing source entities because it shouldn't happen + assert(!(sourceEntity == NULL && packet->sourceId >= 0)); - if (boat) boat->setDoLerp(false); - } else if (boat) { - boat->setDoLerp(true); + deferredEntityLinkPackets.push_back(DeferredEntityLinkPacket(packet)); + return; } - if (rider == NULL) return; - rider->ride(ridden); + if (packet->type == SetEntityLinkPacket::RIDING) { + bool displayMountMessage = false; + if (packet->sourceId == Minecraft::GetInstance() + ->localplayers[m_userIndex] + .get() + ->entityId) { + sourceEntity = Minecraft::GetInstance()->localplayers[m_userIndex]; + + if (destEntity != NULL && destEntity->instanceof(eTYPE_BOAT)) + (std::dynamic_pointer_cast(destEntity))->setDoLerp(false); + + displayMountMessage = + (sourceEntity->riding == NULL && destEntity != NULL); + } else if (destEntity != NULL && destEntity->instanceof(eTYPE_BOAT)) { + (std::dynamic_pointer_cast(destEntity))->setDoLerp(true); + } + + if (sourceEntity == NULL) return; + + sourceEntity->ride(destEntity); + + // 4J TODO: pretty sure this message is a tooltip so not needed + /* + if (displayMountMessage) { + Options options = minecraft.options; + minecraft.gui.setOverlayMessage(I18n.get("mount.onboard", + Options.getTranslatedKeyMessage(options.keySneak.key)), false); + } + */ + } else if (packet->type == SetEntityLinkPacket::LEASH) { + if ((sourceEntity != NULL) && sourceEntity->instanceof(eTYPE_MOB)) { + if (destEntity != NULL) { + (std::dynamic_pointer_cast(sourceEntity)) + ->setLeashedTo(destEntity, false); + } else { + (std::dynamic_pointer_cast(sourceEntity)) + ->dropLeash(false, false); + } + } + } } void ClientConnection::handleEntityEvent( @@ -2475,9 +2679,8 @@ void ClientConnection::handleTextureAndGeometry( void ClientConnection::handleTextureChange( std::shared_ptr packet) { std::shared_ptr e = getEntity(packet->id); - if (e == NULL) return; + if ((e == NULL) || !e->instanceof(eTYPE_PLAYER)) return; std::shared_ptr player = std::dynamic_pointer_cast(e); - if (e == NULL) return; bool isLocalPlayer = false; for (int i = 0; i < XUSER_MAX_COUNT; i++) { @@ -2740,34 +2943,91 @@ void ClientConnection::handleContainerOpen( std::shared_ptr player = minecraft->localplayers[m_userIndex]; switch (packet->type) { - case ContainerOpenPacket::CONTAINER: { + case ContainerOpenPacket::BONUS_CHEST: + case ContainerOpenPacket::LARGE_CHEST: + case ContainerOpenPacket::ENDER_CHEST: + case ContainerOpenPacket::CONTAINER: + case ContainerOpenPacket::MINECART_CHEST: { + int chestString; + switch (packet->type) { + case ContainerOpenPacket::MINECART_CHEST: + chestString = IDS_ITEM_MINECART; + break; + case ContainerOpenPacket::BONUS_CHEST: + chestString = IDS_BONUS_CHEST; + break; + case ContainerOpenPacket::LARGE_CHEST: + chestString = IDS_CHEST_LARGE; + break; + case ContainerOpenPacket::ENDER_CHEST: + chestString = IDS_TILE_ENDERCHEST; + break; + case ContainerOpenPacket::CONTAINER: + chestString = IDS_CHEST; + break; + default: + assert(false); + chestString = -1; + break; + } + if (player->openContainer(std::shared_ptr( - new SimpleContainer(packet->title, packet->size)))) { + new SimpleContainer(chestString, packet->title, + packet->customName, packet->size)))) { + player->containerMenu->containerId = packet->containerId; + } else { + failed = true; + } + } break; + case ContainerOpenPacket::HOPPER: { + std::shared_ptr hopper = + std::shared_ptr(new HopperTileEntity()); + if (packet->customName) hopper->setCustomName(packet->title); + if (player->openHopper(hopper)) { player->containerMenu->containerId = packet->containerId; } else { failed = true; } } break; case ContainerOpenPacket::FURNACE: { - if (player->openFurnace(std::shared_ptr( - new FurnaceTileEntity()))) { + std::shared_ptr furnace = + std::shared_ptr(new FurnaceTileEntity()); + if (packet->customName) furnace->setCustomName(packet->title); + if (player->openFurnace(furnace)) { player->containerMenu->containerId = packet->containerId; } else { failed = true; } } break; case ContainerOpenPacket::BREWING_STAND: { - if (player->openBrewingStand( - std::shared_ptr( - new BrewingStandTileEntity()))) { + std::shared_ptr brewingStand = + std::shared_ptr( + new BrewingStandTileEntity()); + if (packet->customName) brewingStand->setCustomName(packet->title); + + if (player->openBrewingStand(brewingStand)) { + player->containerMenu->containerId = packet->containerId; + } else { + failed = true; + } + } break; + case ContainerOpenPacket::DROPPER: { + std::shared_ptr dropper = + std::shared_ptr(new DropperTileEntity()); + if (packet->customName) dropper->setCustomName(packet->title); + + if (player->openTrap(dropper)) { player->containerMenu->containerId = packet->containerId; } else { failed = true; } } break; case ContainerOpenPacket::TRAP: { - if (player->openTrap(std::shared_ptr( - new DispenserTileEntity()))) { + std::shared_ptr dispenser = + std::shared_ptr(new DispenserTileEntity()); + if (packet->customName) dispenser->setCustomName(packet->title); + + if (player->openTrap(dispenser)) { player->containerMenu->containerId = packet->containerId; } else { failed = true; @@ -2783,9 +3043,10 @@ void ClientConnection::handleContainerOpen( } } break; case ContainerOpenPacket::ENCHANTMENT: { - if (player->startEnchanting(Mth::floor(player->x), - Mth::floor(player->y), - Mth::floor(player->z))) { + if (player->startEnchanting( + Mth::floor(player->x), Mth::floor(player->y), + Mth::floor(player->z), + packet->customName ? packet->title : L"")) { player->containerMenu->containerId = packet->containerId; } else { failed = true; @@ -2796,7 +3057,19 @@ void ClientConnection::handleContainerOpen( std::shared_ptr( new ClientSideMerchant(player, packet->title)); csm->createContainer(); - if (player->openTrading(csm)) { + if (player->openTrading(csm, + packet->customName ? packet->title : L"")) { + player->containerMenu->containerId = packet->containerId; + } else { + failed = true; + } + } break; + case ContainerOpenPacket::BEACON: { + std::shared_ptr beacon = + std::shared_ptr(new BeaconTileEntity()); + if (packet->customName) beacon->setCustomName(packet->title); + + if (player->openBeacon(beacon)) { player->containerMenu->containerId = packet->containerId; } else { failed = true; @@ -2811,7 +3084,41 @@ void ClientConnection::handleContainerOpen( failed = true; } } break; - }; + case ContainerOpenPacket::HORSE: { + std::shared_ptr entity = + std::dynamic_pointer_cast( + getEntity(packet->entityId)); + int iTitle = IDS_CONTAINER_ANIMAL; + switch (entity->getType()) { + case EntityHorse::TYPE_DONKEY: + iTitle = IDS_DONKEY; + break; + case EntityHorse::TYPE_MULE: + iTitle = IDS_MULE; + break; + default: + break; + }; + if (player->openHorseInventory( + std::dynamic_pointer_cast(entity), + std::shared_ptr( + new AnimalChest(iTitle, packet->title, + packet->customName, packet->size)))) { + player->containerMenu->containerId = packet->containerId; + } else { + failed = true; + } + } break; + case ContainerOpenPacket::FIREWORKS: { + if (player->openFireworks(Mth::floor(player->x), + Mth::floor(player->y), + Mth::floor(player->z))) { + player->containerMenu->containerId = packet->containerId; + } else { + failed = true; + } + } break; + } if (failed) { // Failed - if we've got a non-inventory container currently here, close @@ -2887,6 +3194,23 @@ void ClientConnection::handleContainerContent( } } +void ClientConnection::handleTileEditorOpen( + std::shared_ptr packet) { + std::shared_ptr tileEntity = + level->getTileEntity(packet->x, packet->y, packet->z); + if (tileEntity != NULL) { + minecraft->localplayers[m_userIndex]->openTextEdit(tileEntity); + } else if (packet->editorType == TileEditorOpenPacket::SIGN) { + std::shared_ptr localSignDummy = + std::shared_ptr(new SignTileEntity()); + localSignDummy->setLevel(level); + localSignDummy->x = packet->x; + localSignDummy->y = packet->y; + localSignDummy->z = packet->z; + minecraft->player->openTextEdit(localSignDummy); + } +} + void ClientConnection::handleSignUpdate( std::shared_ptr packet) { app.DebugPrintf("ClientConnection::handleSignUpdate - "); @@ -2928,19 +3252,18 @@ void ClientConnection::handleTileEntityData( std::dynamic_pointer_cast(te) != NULL) { std::dynamic_pointer_cast(te)->load( packet->tag); - } - // else if (packet.type == TileEntityDataPacket.TYPE_ADV_COMMAND && - // (te instanceof CommandBlockEntity)) - //{ - // ((CommandBlockEntity) te).load(packet.tag); - // } - // else if (packet.type == TileEntityDataPacket.TYPE_BEACON && (te - // instanceof BeaconTileEntity)) - //{ - // ((BeaconTileEntity) te).load(packet.tag); - // } - else if (packet->type == TileEntityDataPacket::TYPE_SKULL && - std::dynamic_pointer_cast(te) != NULL) { + } else if (packet->type == TileEntityDataPacket::TYPE_ADV_COMMAND && + std::dynamic_pointer_cast(te) != + NULL) { + std::dynamic_pointer_cast(te)->load( + packet->tag); + } else if (packet->type == TileEntityDataPacket::TYPE_BEACON && + std::dynamic_pointer_cast(te) != + NULL) { + std::dynamic_pointer_cast(te)->load( + packet->tag); + } else if (packet->type == TileEntityDataPacket::TYPE_SKULL && + std::dynamic_pointer_cast(te) != NULL) { std::dynamic_pointer_cast(te)->load( packet->tag); } @@ -2972,7 +3295,7 @@ void ClientConnection::handleSetEquippedItem( void ClientConnection::handleContainerClose( std::shared_ptr packet) { - minecraft->localplayers[m_userIndex]->closeContainer(); + minecraft->localplayers[m_userIndex]->clientSideCloseContainer(); } void ClientConnection::handleTileEvent( @@ -3062,6 +3385,12 @@ void ClientConnection::handleGameEvent( } } else if (event == GameEventPacket::STOP_SAVING) { if (!g_NetworkManager.IsHost()) app.SetGameStarted(true); + } else if (event == GameEventPacket::SUCCESSFUL_BOW_HIT) { + std::shared_ptr player = + minecraft->localplayers[m_userIndex]; + level->playLocalSound(player->x, player->y + player->getHeadHeight(), + player->z, eSoundType_RANDOM_BOW_HIT, 0.18f, + 0.45f, false); } } @@ -3078,28 +3407,28 @@ void ClientConnection::handleComplexItemData( void ClientConnection::handleLevelEvent( std::shared_ptr packet) { - switch (packet->type) { - case LevelEvent::SOUND_DRAGON_DEATH: { - Minecraft* pMinecraft = Minecraft::GetInstance(); - for (unsigned int i = 0; i < XUSER_MAX_COUNT; ++i) { - if (pMinecraft->localplayers[i] != NULL && - pMinecraft->localplayers[i]->level != NULL && - pMinecraft->localplayers[i]->level->dimension->id == 1) { - pMinecraft->localplayers[i]->awardStat( - GenericStats::completeTheEnd(), - GenericStats::param_noArgs()); - } + if (packet->type == LevelEvent::SOUND_DRAGON_DEATH) { + for (unsigned int i = 0; i < XUSER_MAX_COUNT; ++i) { + if (minecraft->localplayers[i] != NULL && + minecraft->localplayers[i]->level != NULL && + minecraft->localplayers[i]->level->dimension->id == 1) { + minecraft->localplayers[i]->awardStat( + GenericStats::completeTheEnd(), + GenericStats::param_noArgs()); } } - minecraft->level->levelEvent(packet->type, packet->x, packet->y, - packet->z, packet->data); - - break; - default: - minecraft->level->levelEvent(packet->type, packet->x, packet->y, - packet->z, packet->data); - break; } + + if (packet->isGlobalEvent()) { + minecraft->level->globalLevelEvent(packet->type, packet->x, packet->y, + packet->z, packet->data); + } else { + minecraft->level->levelEvent(packet->type, packet->x, packet->y, + packet->z, packet->data); + } + + minecraft->level->levelEvent(packet->type, packet->x, packet->y, packet->z, + packet->data); } void ClientConnection::handleAwardStat( @@ -3111,20 +3440,25 @@ void ClientConnection::handleAwardStat( void ClientConnection::handleUpdateMobEffect( std::shared_ptr packet) { std::shared_ptr e = getEntity(packet->entityId); - if (e == NULL || std::dynamic_pointer_cast(e) == NULL) return; + if ((e == NULL) || !e->instanceof(eTYPE_LIVINGENTITY)) return; - (std::dynamic_pointer_cast(e)) - ->addEffect(new MobEffectInstance(packet->effectId, - packet->effectDurationTicks, - packet->effectAmplifier)); + //( std::dynamic_pointer_cast(e) )->addEffect(new + // MobEffectInstance(packet->effectId, packet->effectDurationTicks, + // packet->effectAmplifier)); + + MobEffectInstance* mobEffectInstance = new MobEffectInstance( + packet->effectId, packet->effectDurationTicks, packet->effectAmplifier); + mobEffectInstance->setNoCounter(packet->isSuperLongDuration()); + std::dynamic_pointer_cast(e)->addEffect(mobEffectInstance); } void ClientConnection::handleRemoveMobEffect( std::shared_ptr packet) { std::shared_ptr e = getEntity(packet->entityId); - if (e == NULL || std::dynamic_pointer_cast(e) == NULL) return; + if ((e == NULL) || !e->instanceof(eTYPE_LIVINGENTITY)) return; - (std::dynamic_pointer_cast(e))->removeEffectNoUpdate(packet->effectId); + (std::dynamic_pointer_cast(e)) + ->removeEffectNoUpdate(packet->effectId); } bool ClientConnection::isServerPacketListener() { return false; } @@ -3149,7 +3483,7 @@ void ClientConnection::handlePlayerInfo( packet->m_playerPrivileges); std::shared_ptr entity = getEntity(packet->m_entityId); - if (entity != NULL && entity->GetType() == eTYPE_PLAYER) { + if (entity != NULL && entity->instanceof(eTYPE_PLAYER)) { std::shared_ptr player = std::dynamic_pointer_cast(entity); player->setPlayerGamePrivilege(Player::ePlayerGamePrivilege_All, @@ -3356,13 +3690,14 @@ void ClientConnection::handlePlayerAbilities( player->abilities.invulnerable = playerAbilitiesPacket->isInvulnerable(); player->abilities.mayfly = playerAbilitiesPacket->canFly(); player->abilities.setFlyingSpeed(playerAbilitiesPacket->getFlyingSpeed()); + player->abilities.setWalkingSpeed(playerAbilitiesPacket->getWalkingSpeed()); } void ClientConnection::handleSoundEvent( std::shared_ptr packet) { - minecraft->level->playLocalSound(packet->getX(), packet->getY(), - packet->getZ(), packet->getSound(), - packet->getVolume(), packet->getPitch()); + minecraft->level->playLocalSound( + packet->getX(), packet->getY(), packet->getZ(), packet->getSound(), + packet->getVolume(), packet->getPitch(), false); } void ClientConnection::handleCustomPayload( @@ -3498,10 +3833,10 @@ int ClientConnection::HostDisconnectReturned( unsigned int uiIDA[2]; uiIDA[0] = IDS_CONFIRM_CANCEL; uiIDA[1] = IDS_CONFIRM_OK; - ui.RequestMessageBox(IDS_TITLE_SAVE_GAME, IDS_CONFIRM_SAVE_GAME, uiIDA, - 2, ProfileManager.GetPrimaryPad(), - &ClientConnection::ExitGameAndSaveReturned, NULL, - app.GetStringTable()); + ui.RequestErrorMessage(IDS_TITLE_SAVE_GAME, IDS_CONFIRM_SAVE_GAME, + uiIDA, 2, ProfileManager.GetPrimaryPad(), + &ClientConnection::ExitGameAndSaveReturned, + NULL); } else #else // Give the player the option to save their game @@ -3514,10 +3849,10 @@ int ClientConnection::HostDisconnectReturned( unsigned int uiIDA[2]; uiIDA[0] = IDS_CONFIRM_CANCEL; uiIDA[1] = IDS_CONFIRM_OK; - ui.RequestMessageBox(IDS_TITLE_SAVE_GAME, IDS_CONFIRM_SAVE_GAME, uiIDA, - 2, ProfileManager.GetPrimaryPad(), - &ClientConnection::ExitGameAndSaveReturned, NULL, - app.GetStringTable()); + ui.RequestErrorMessage(IDS_TITLE_SAVE_GAME, IDS_CONFIRM_SAVE_GAME, + uiIDA, 2, ProfileManager.GetPrimaryPad(), + &ClientConnection::ExitGameAndSaveReturned, + NULL); } else #endif { @@ -3563,3 +3898,211 @@ std::wstring ClientConnection::GetDisplayNameByGamertag(std::wstring gamertag) { return gamertag; #endif } + +void ClientConnection::handleAddObjective( + std::shared_ptr packet) { +#if 0 + Scoreboard scoreboard = level->getScoreboard(); + + if (packet->method == SetObjectivePacket::METHOD_ADD) + { + Objective objective = scoreboard->addObjective(packet->objectiveName, ObjectiveCriteria::DUMMY); + objective->setDisplayName(packet->displayName); + } + else + { + Objective objective = scoreboard->getObjective(packet->objectiveName); + + if (packet->method == SetObjectivePacket::METHOD_REMOVE) + { + scoreboard->removeObjective(objective); + } + else if (packet->method == SetObjectivePacket::METHOD_CHANGE) + { + objective->setDisplayName(packet->displayName); + } + } +#endif +} + +void ClientConnection::handleSetScore(std::shared_ptr packet) { +#if 0 + Scoreboard scoreboard = level->getScoreboard(); + Objective objective = scoreboard->getObjective(packet->objectiveName); + + if (packet->method == SetScorePacket::METHOD_CHANGE) + { + Score score = scoreboard->getPlayerScore(packet->owner, objective); + score->setScore(packet->score); + } + else if (packet->method == SetScorePacket::METHOD_REMOVE) + { + scoreboard->resetPlayerScore(packet->owner); + } +#endif +} + +void ClientConnection::handleSetDisplayObjective( + std::shared_ptr packet) { +#if 0 + Scoreboard scoreboard = level->getScoreboard(); + + if (packet->objectiveName->length() == 0) + { + scoreboard->setDisplayObjective(packet->slot, null); + } + else + { + Objective objective = scoreboard->getObjective(packet->objectiveName); + scoreboard->setDisplayObjective(packet->slot, objective); + } +#endif +} + +void ClientConnection::handleSetPlayerTeamPacket( + std::shared_ptr packet) { +#if 0 + Scoreboard scoreboard = level->getScoreboard(); + PlayerTeam *team; + + if (packet->method == SetPlayerTeamPacket::METHOD_ADD) + { + team = scoreboard->addPlayerTeam(packet->name); + } + else + { + team = scoreboard->getPlayerTeam(packet->name); + } + + if (packet->method == SetPlayerTeamPacket::METHOD_ADD || packet->method == SetPlayerTeamPacket::METHOD_CHANGE) + { + team->setDisplayName(packet->displayName); + team->setPrefix(packet->prefix); + team->setSuffix(packet->suffix); + team->unpackOptions(packet->options); + } + + if (packet->method == SetPlayerTeamPacket::METHOD_ADD || packet->method == SetPlayerTeamPacket::METHOD_JOIN) + { + for (int i = 0; i < packet->players.size(); i++) + { + scoreboard->addPlayerToTeam(packet->players[i], team); + } + } + + if (packet->method == SetPlayerTeamPacket::METHOD_LEAVE) + { + for (int i = 0; i < packet->players.size(); i++) + { + scoreboard->removePlayerFromTeam(packet->players[i], team); + } + } + + if (packet->method == SetPlayerTeamPacket::METHOD_REMOVE) + { + scoreboard->removePlayerTeam(team); + } +#endif +} + +void ClientConnection::handleParticleEvent( + std::shared_ptr packet) { + for (int i = 0; i < packet->getCount(); i++) { + double xVarience = random->nextGaussian() * packet->getXDist(); + double yVarience = random->nextGaussian() * packet->getYDist(); + double zVarience = random->nextGaussian() * packet->getZDist(); + double xa = random->nextGaussian() * packet->getMaxSpeed(); + double ya = random->nextGaussian() * packet->getMaxSpeed(); + double za = random->nextGaussian() * packet->getMaxSpeed(); + + // TODO: determine particle ID from name + assert(0); + ePARTICLE_TYPE particleId = eParticleType_heart; + + level->addParticle(particleId, packet->getX() + xVarience, + packet->getY() + yVarience, + packet->getZ() + zVarience, xa, ya, za); + } +} + +void ClientConnection::handleUpdateAttributes( + std::shared_ptr packet) { + std::shared_ptr entity = getEntity(packet->getEntityId()); + if (entity == NULL) return; + + if (!entity->instanceof(eTYPE_LIVINGENTITY)) { + // Entity is not a living entity! + assert(0); + } + + BaseAttributeMap* attributes = + (std::dynamic_pointer_cast(entity))->getAttributes(); + std::unordered_set + attributeSnapshots = packet->getValues(); + for (AUTO_VAR(it, attributeSnapshots.begin()); + it != attributeSnapshots.end(); ++it) { + UpdateAttributesPacket::AttributeSnapshot* attribute = *it; + AttributeInstance* instance = + attributes->getInstance(attribute->getId()); + + if (instance == NULL) { + // 4J - TODO: revisit, not familiar with the attribute system, why + // are we passing in MIN_NORMAL (Java's smallest non-zero value + // conforming to IEEE Standard 754 (?)) and MAX_VALUE + instance = attributes->registerAttribute(new RangedAttribute( + attribute->getId(), 0, Double::MIN_NORMAL, Double::MAX_VALUE)); + } + + instance->setBaseValue(attribute->getBase()); + instance->removeModifiers(); + + std::unordered_set* modifiers = + attribute->getModifiers(); + + for (AUTO_VAR(it2, modifiers->begin()); it2 != modifiers->end(); + ++it2) { + AttributeModifier* modifier = *it2; + instance->addModifier( + new AttributeModifier(modifier->getId(), modifier->getAmount(), + modifier->getOperation())); + } + } +} + +// 4J: Check for deferred entity link packets related to this entity ID and +// handle them +void ClientConnection::checkDeferredEntityLinkPackets(int newEntityId) { + if (deferredEntityLinkPackets.empty()) return; + + for (int i = 0; i < deferredEntityLinkPackets.size(); i++) { + DeferredEntityLinkPacket* deferred = &deferredEntityLinkPackets[i]; + + bool remove = false; + + // Only consider recently deferred packets + int tickInterval = GetTickCount() - deferred->m_recievedTick; + if (tickInterval < MAX_ENTITY_LINK_DEFERRAL_INTERVAL) { + // Note: we assume it's the destination entity + if (deferred->m_packet->destId == newEntityId) { + handleEntityLinkPacket(deferred->m_packet); + remove = true; + } + } else { + // This is an old packet, remove (shouldn't really come up but seems + // prudent) + remove = true; + } + + if (remove) { + deferredEntityLinkPackets.erase(deferredEntityLinkPackets.begin() + + i); + i--; + } + } +} + +ClientConnection::DeferredEntityLinkPacket::DeferredEntityLinkPacket( + std::shared_ptr packet) { + m_recievedTick = GetTickCount(); + m_packet = packet; +} \ No newline at end of file diff --git a/Minecraft.Client/Network/ClientConnection.h b/Minecraft.Client/Network/ClientConnection.h index 97d8855ae..f31ea0a58 100644 --- a/Minecraft.Client/Network/ClientConnection.h +++ b/Minecraft.Client/Network/ClientConnection.h @@ -1,12 +1,10 @@ #pragma once #include "../../Minecraft.World/Headers/net.minecraft.network.h" - class Minecraft; class MultiPlayerLevel; class SavedDataStorage; class Socket; class MultiplayerLocalPlayer; -class SetRidingPacket; class ClientConnection : public PacketListener { private: @@ -67,6 +65,8 @@ public: virtual void handleAddPlayer(std::shared_ptr packet); virtual void handleTeleportEntity( std::shared_ptr packet); + virtual void handleSetCarriedItem( + std::shared_ptr packet); virtual void handleMoveEntity(std::shared_ptr packet); virtual void handleRotateMob(std::shared_ptr packet); virtual void handleMoveEntitySmall( @@ -104,7 +104,8 @@ public: virtual void handleAddMob(std::shared_ptr packet); virtual void handleSetTime(std::shared_ptr packet); virtual void handleSetSpawn(std::shared_ptr packet); - virtual void handleRidePacket(std::shared_ptr packet); + virtual void handleEntityLinkPacket( + std::shared_ptr packet); virtual void handleEntityEvent(std::shared_ptr packet); private: @@ -124,6 +125,8 @@ public: virtual void handleContainerAck(std::shared_ptr packet); virtual void handleContainerContent( std::shared_ptr packet); + virtual void handleTileEditorOpen( + std::shared_ptr packet); virtual void handleSignUpdate(std::shared_ptr packet); virtual void handleTileEntityData( std::shared_ptr packet); @@ -181,4 +184,30 @@ public: void displayPrivilegeChanges(std::shared_ptr player, unsigned int oldPrivileges); -}; + + virtual void handleAddObjective(std::shared_ptr packet); + virtual void handleSetScore(std::shared_ptr packet); + virtual void handleSetDisplayObjective( + std::shared_ptr packet); + virtual void handleSetPlayerTeamPacket( + std::shared_ptr packet); + virtual void handleParticleEvent( + std::shared_ptr packet); + virtual void handleUpdateAttributes( + std::shared_ptr packet); + +private: + // 4J: Entity link packet deferred + class DeferredEntityLinkPacket { + public: + DWORD m_recievedTick; + std::shared_ptr m_packet; + + DeferredEntityLinkPacket(std::shared_ptr packet); + }; + + std::vector deferredEntityLinkPackets; + static const int MAX_ENTITY_LINK_DEFERRAL_INTERVAL = 1000; + + void checkDeferredEntityLinkPackets(int newEntityId); +}; \ No newline at end of file diff --git a/Minecraft.Client/Network/MultiPlayerChunkCache.cpp b/Minecraft.Client/Network/MultiPlayerChunkCache.cpp index 74648c2ad..8a95be10f 100644 --- a/Minecraft.Client/Network/MultiPlayerChunkCache.cpp +++ b/Minecraft.Client/Network/MultiPlayerChunkCache.cpp @@ -44,7 +44,7 @@ MultiPlayerChunkCache::MultiPlayerChunkCache(Level* level) { for (int z = 0; z < 16; z++) { unsigned char tileId = 0; if (y <= (level->getSeaLevel() - 10)) - tileId = Tile::rock_Id; + tileId = Tile::stone_Id; else if (y < level->getSeaLevel()) tileId = Tile::calmWater_Id; @@ -195,8 +195,7 @@ LevelChunk* MultiPlayerChunkCache::create(int x, int z) { // 4J - changed to use new methods for lighting chunk->setSkyLightDataAllBright(); - // Arrays::fill(chunk->skyLight->data, (uint8_t) - //255); + // Arrays::fill(chunk->skyLight->data, (byte) 255); } chunk->loaded = true; @@ -278,6 +277,9 @@ TilePos* MultiPlayerChunkCache::findNearestMapFeature( return NULL; } +void MultiPlayerChunkCache::recreateLogicStructuresForChunk(int chunkX, + int chunkZ) {} + std::wstring MultiPlayerChunkCache::gatherStats() { EnterCriticalSection(&m_csLoadCreate); int size = (int)loadedChunkList.size(); diff --git a/Minecraft.Client/Network/MultiPlayerChunkCache.h b/Minecraft.Client/Network/MultiPlayerChunkCache.h index 09710048d..ef14a168f 100644 --- a/Minecraft.Client/Network/MultiPlayerChunkCache.h +++ b/Minecraft.Client/Network/MultiPlayerChunkCache.h @@ -44,6 +44,7 @@ public: virtual TilePos* findNearestMapFeature(Level* level, const std::wstring& featureName, int x, int y, int z); + virtual void recreateLogicStructuresForChunk(int chunkX, int chunkZ); virtual void dataReceived(int x, int z); // 4J added virtual LevelChunk** getCache() { return cache; } // 4J added diff --git a/Minecraft.Client/Network/PendingConnection.cpp b/Minecraft.Client/Network/PendingConnection.cpp index bd86e35e4..08e1bb031 100644 --- a/Minecraft.Client/Network/PendingConnection.cpp +++ b/Minecraft.Client/Network/PendingConnection.cpp @@ -258,3 +258,5 @@ std::wstring PendingConnection::getName() { } bool PendingConnection::isServerPacketListener() { return true; } + +bool PendingConnection::isDisconnected() { return done; } \ No newline at end of file diff --git a/Minecraft.Client/Network/PendingConnection.h b/Minecraft.Client/Network/PendingConnection.h index 8ee23c434..aab9e0ed3 100644 --- a/Minecraft.Client/Network/PendingConnection.h +++ b/Minecraft.Client/Network/PendingConnection.h @@ -46,6 +46,7 @@ public: void send(std::shared_ptr packet); std::wstring getName(); virtual bool isServerPacketListener(); + virtual bool isDisconnected(); private: void sendPreLoginResponse(); diff --git a/Minecraft.Client/Network/PlayerChunkMap.cpp b/Minecraft.Client/Network/PlayerChunkMap.cpp index 95812270b..be55d0174 100644 --- a/Minecraft.Client/Network/PlayerChunkMap.cpp +++ b/Minecraft.Client/Network/PlayerChunkMap.cpp @@ -7,6 +7,7 @@ #include "../MinecraftServer.h" #include "../../Minecraft.World/Headers/net.minecraft.network.packet.h" #include "../../Minecraft.World/Headers/net.minecraft.world.level.h" +#include "../../Minecraft.World/Headers/net.minecraft.world.level.chunk.h" #include "../../Minecraft.World/Headers/net.minecraft.world.level.tile.h" #include "../../Minecraft.World/Util/ArrayWithLength.h" #include "../../Minecraft.World/Platform/System.h" @@ -23,19 +24,12 @@ PlayerChunkMap::PlayerChunk::PlayerChunk(int x, int z, PlayerChunkMap* pcm) parent = pcm; // 4J added ticksToNextRegionUpdate = 0; // 4J added prioritised = false; // 4J added + firstInhabitedTime = 0; parent->getLevel()->cache->create(x, z); - // 4J - added make sure our lights are up to date as soon as we make it. - // This is of particular concern for local clients, who have their data - // shared as soon as the chunkvisibilitypacket is sent, and so could - // potentially create render data for this chunk before it has been properly - // lit. - while (parent->getLevel()->updateLights()); } -PlayerChunkMap::PlayerChunk::~PlayerChunk() { - delete[] changedTiles.data; // 4jcraft, changed to [] -} +PlayerChunkMap::PlayerChunk::~PlayerChunk() { delete changedTiles.data; } // 4J added - construct an an array of flags that indicate which entities are // still waiting to have network packets sent out to say that they have been @@ -77,6 +71,10 @@ void PlayerChunkMap::PlayerChunk::add(std::shared_ptr player, player->connection->send(std::shared_ptr( new ChunkVisibilityPacket(pos.x, pos.z, true))); + if (players.empty()) { + firstInhabitedTime = parent->level->getGameTime(); + } + players.push_back(player); player->chunksToSend.push_back(pos); @@ -103,7 +101,14 @@ void PlayerChunkMap::PlayerChunk::remove(std::shared_ptr player) { players.erase(it); if (players.size() == 0) { - __int64 id = (pos.x + 0x7fffffffLL) | ((pos.z + 0x7fffffffLL) << 32); + { + LevelChunk* chunk = parent->level->getChunk(pos.x, pos.z); + updateInhabitedTime(chunk); + AUTO_VAR(it, find(parent->knownChunks.begin(), + parent->knownChunks.end(), this)); + if (it != parent->knownChunks.end()) parent->knownChunks.erase(it); + } + int64_t id = (pos.x + 0x7fffffffLL) | ((pos.z + 0x7fffffffLL) << 32); AUTO_VAR(it, parent->chunks.find(id)); if (it != parent->chunks.end()) { toDelete = it->second; // Don't delete until the end of the @@ -157,6 +162,16 @@ void PlayerChunkMap::PlayerChunk::remove(std::shared_ptr player) { delete toDelete; } +void PlayerChunkMap::PlayerChunk::updateInhabitedTime() { + updateInhabitedTime(parent->level->getChunk(pos.x, pos.z)); +} + +void PlayerChunkMap::PlayerChunk::updateInhabitedTime(LevelChunk* chunk) { + chunk->inhabitedTime += parent->level->getGameTime() - firstInhabitedTime; + + firstInhabitedTime = parent->level->getGameTime(); +} + void PlayerChunkMap::PlayerChunk::tileChanged(int x, int y, int z) { if (changes == 0) { parent->changedChunks.push_back(this); @@ -386,6 +401,7 @@ PlayerChunkMap::PlayerChunkMap(ServerLevel* level, int dimension, int radius) { this->radius = radius; this->level = level; this->dimension = dimension; + lastInhabitedUpdate = 0; } PlayerChunkMap::~PlayerChunkMap() { @@ -397,6 +413,22 @@ PlayerChunkMap::~PlayerChunkMap() { ServerLevel* PlayerChunkMap::getLevel() { return level; } void PlayerChunkMap::tick() { + int64_t time = level->getGameTime(); + + if (time - lastInhabitedUpdate > Level::TICKS_PER_DAY / 3) { + lastInhabitedUpdate = time; + + for (int i = 0; i < knownChunks.size(); i++) { + PlayerChunk* chunk = knownChunks.at(i); + + // 4J Stu - Going to let our changeChunks handler below deal with + // this + // chunk.broadcastChanges(); + + chunk->updateInhabitedTime(); + } + } + // 4J - some changes here so that we only send one region update per tick. // The chunks themselves also limit their resend rate to once every // MIN_TICKS_BETWEEN_REGION_UPDATE ticks @@ -432,13 +464,13 @@ void PlayerChunkMap::tick() { } bool PlayerChunkMap::hasChunk(int x, int z) { - __int64 id = (x + 0x7fffffffLL) | ((z + 0x7fffffffLL) << 32); + int64_t id = (x + 0x7fffffffLL) | ((z + 0x7fffffffLL) << 32); return chunks.find(id) != chunks.end(); } PlayerChunkMap::PlayerChunk* PlayerChunkMap::getChunk(int x, int z, bool create) { - __int64 id = (x + 0x7fffffffLL) | ((z + 0x7fffffffLL) << 32); + int64_t id = (x + 0x7fffffffLL) | ((z + 0x7fffffffLL) << 32); AUTO_VAR(it, chunks.find(id)); PlayerChunk* chunk = NULL; @@ -447,6 +479,7 @@ PlayerChunkMap::PlayerChunk* PlayerChunkMap::getChunk(int x, int z, } else if (create) { chunk = new PlayerChunk(x, z, this); chunks[id] = chunk; + knownChunks.push_back(chunk); } return chunk; @@ -456,7 +489,7 @@ PlayerChunkMap::PlayerChunk* PlayerChunkMap::getChunk(int x, int z, // doesn't exist, queue a request for it to be created. void PlayerChunkMap::getChunkAndAddPlayer( int x, int z, std::shared_ptr player) { - __int64 id = (x + 0x7fffffffLL) | ((z + 0x7fffffffLL) << 32); + int64_t id = (x + 0x7fffffffLL) | ((z + 0x7fffffffLL) << 32); AUTO_VAR(it, chunks.find(id)); if (it != chunks.end()) { @@ -476,7 +509,7 @@ void PlayerChunkMap::getChunkAndRemovePlayer( return; } } - __int64 id = (x + 0x7fffffffLL) | ((z + 0x7fffffffLL) << 32); + int64_t id = (x + 0x7fffffffLL) | ((z + 0x7fffffffLL) << 32); AUTO_VAR(it, chunks.find(id)); if (it != chunks.end()) { @@ -510,7 +543,6 @@ void PlayerChunkMap::tickAddRequests(std::shared_ptr player) { if (itNearest != addRequests.end()) { getChunk(itNearest->x, itNearest->z, true)->add(itNearest->player); addRequests.erase(itNearest); - return; } } } @@ -582,6 +614,14 @@ void PlayerChunkMap::add(std::shared_ptr player) { minX = maxX = xc; minZ = maxZ = zc; + // 4J - added so that we don't fully create/send every chunk at this stage. + // Particularly since moving on to large worlds, where we can be adding 1024 + // chunks here of which a large % might need to be fully created, this can + // take a long time. Instead use the getChunkAndAddPlayer for anything but + // the central region of chunks, which adds them to a queue of chunks which + // are added one per tick per player. + const int maxLegSizeToAddNow = 14; + // All but the last leg for (int legSize = 1; legSize <= size * 2; legSize++) { for (int leg = 0; leg < 2; leg++) { @@ -594,12 +634,19 @@ void PlayerChunkMap::add(std::shared_ptr player) { int targetX, targetZ; targetX = xc + dx; targetZ = zc + dz; - if (targetX > maxX) maxX = targetX; - if (targetX < minX) minX = targetX; - if (targetZ > maxZ) maxZ = targetZ; - if (targetZ < minZ) minZ = targetZ; - getChunk(targetX, targetZ, true)->add(player, false); + if ((legSize < maxLegSizeToAddNow) || + ((legSize == maxLegSizeToAddNow) && + ((leg == 0) || (k < (legSize - 1))))) { + if (targetX > maxX) maxX = targetX; + if (targetX < minX) minX = targetX; + if (targetZ > maxZ) maxZ = targetZ; + if (targetZ < minZ) minZ = targetZ; + + getChunk(targetX, targetZ, true)->add(player, false); + } else { + getChunkAndAddPlayer(targetX, targetZ, player); + } } } } @@ -613,12 +660,16 @@ void PlayerChunkMap::add(std::shared_ptr player) { int targetX, targetZ; targetX = xc + dx; targetZ = zc + dz; - if (targetX > maxX) maxX = targetX; - if (targetX < minX) minX = targetX; - if (targetZ > maxZ) maxZ = targetZ; - if (targetZ < minZ) minZ = targetZ; + if ((size * 2) <= maxLegSizeToAddNow) { + if (targetX > maxX) maxX = targetX; + if (targetX < minX) minX = targetX; + if (targetZ > maxZ) maxZ = targetZ; + if (targetZ < minZ) minZ = targetZ; - getChunk(targetX, targetZ, true)->add(player, false); + getChunk(targetX, targetZ, true)->add(player, false); + } else { + getChunkAndAddPlayer(targetX, targetZ, player); + } } // CraftBukkit end @@ -753,4 +804,4 @@ void PlayerChunkMap::setRadius(int newRadius) { assert(radius >= MIN_VIEW_DISTANCE); this->radius = newRadius; } -} +} \ No newline at end of file diff --git a/Minecraft.Client/Network/PlayerChunkMap.h b/Minecraft.Client/Network/PlayerChunkMap.h index 61ace4de2..a0315897a 100644 --- a/Minecraft.Client/Network/PlayerChunkMap.h +++ b/Minecraft.Client/Network/PlayerChunkMap.h @@ -44,6 +44,7 @@ public: int zChangeMin, zChangeMax; int ticksToNextRegionUpdate; // 4J added bool prioritised; // 4J added + int64_t firstInhabitedTime; public: PlayerChunk(int x, int z, PlayerChunkMap* pcm); @@ -53,6 +54,12 @@ public: // one much smaller packet void add(std::shared_ptr player, bool sendPacket = true); void remove(std::shared_ptr player); + void updateInhabitedTime(); + + private: + void updateInhabitedTime(LevelChunk* chunk); + + public: void tileChanged(int x, int y, int z); void prioritiseTileChanges(); // 4J added void broadcast(std::shared_ptr packet); @@ -67,15 +74,17 @@ public: void flagEntitiesToBeRemoved(unsigned int* flags, bool* removedFound); // 4J added private: - std::unordered_map<__int64, PlayerChunk*, LongKeyHash, LongKeyEq> + std::unordered_map chunks; // 4J - was LongHashMap std::vector changedChunks; + std::vector knownChunks; std::vector addRequests; // 4J added void tickAddRequests(std::shared_ptr player); // 4J added ServerLevel* level; int radius; int dimension; + int64_t lastInhabitedUpdate; public: PlayerChunkMap(ServerLevel* level, int dimension, int radius); diff --git a/Minecraft.Client/Network/PlayerConnection.cpp b/Minecraft.Client/Network/PlayerConnection.cpp index bc6a1be76..0e5038d86 100644 --- a/Minecraft.Client/Network/PlayerConnection.cpp +++ b/Minecraft.Client/Network/PlayerConnection.cpp @@ -15,6 +15,7 @@ #include "../../Minecraft.World/Headers/net.minecraft.world.inventory.h" #include "../../Minecraft.World/Headers/net.minecraft.world.level.tile.entity.h" #include "../../Minecraft.World/Headers/net.minecraft.world.level.saveddata.h" +#include "../../Minecraft.World/Headers/net.minecraft.world.entity.animal.h" #include "../../Minecraft.World/Headers/net.minecraft.network.h" #include "../../Minecraft.World/Headers/net.minecraft.world.food.h" #include "../../Minecraft.World/Util/AABB.h" @@ -55,7 +56,7 @@ PlayerConnection::PlayerConnection(MinecraftServer* server, connection->setListener(this); this->player = player; // player->connection = this; // 4J - moved out as we can't - //assign in a ctor + // assign in a ctor InitializeCriticalSection(&done_cs); m_bCloseOnTick = false; @@ -95,9 +96,6 @@ void PlayerConnection::tick() { send(std::shared_ptr( new KeepAlivePacket(lastKeepAliveId))); } - // if (!didTick) { - // player->doTick(false); - // } if (chatSpamTickCount > 0) { chatSpamTickCount--; @@ -142,9 +140,8 @@ void PlayerConnection::disconnect(DisconnectPacket::eDisconnectReason reason) { void PlayerConnection::handlePlayerInput( std::shared_ptr packet) { - player->setPlayerInput(packet->getXa(), packet->getYa(), - packet->isJumping(), packet->isSneaking(), - packet->getXRot(), packet->getYRot()); + player->setPlayerInput(packet->getXxa(), packet->getYya(), + packet->isJumping(), packet->isSneaking()); } void PlayerConnection::handleMovePlayer( @@ -172,45 +169,27 @@ void PlayerConnection::handleMovePlayer( 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 " - L"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->ySlideOffset = 0; 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; + + // player may have been kicked off the mount during the tick, so + // only copy valid coordinates if the player still is "synched" + if (synched) { + xLastOk = player->x; + yLastOk = player->y; + zLastOk = player->z; + } ((Level*)level)->tick(player); return; @@ -283,12 +262,12 @@ void PlayerConnection::handleMovePlayer( // 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; + // 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; } */ @@ -332,10 +311,10 @@ player->xRot); return; 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); + // 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); @@ -359,8 +338,8 @@ player->xRot); return; if (oyDist >= (-0.5f / 16.0f)) { aboveGroundTickCount++; if (aboveGroundTickCount > 80) { -// logger.warning(player->name + " was kicked for floating -// too long!"); + // 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()); @@ -376,6 +355,8 @@ player->xRot); return; player->onGround = packet->onGround; server->getPlayers()->move(player); player->doCheckFallDamage(player->y - startY, packet->onGround); + } else if ((tickCount % SharedConstants::TICKS_PER_SECOND) == 0) { + teleport(xLastOk, yLastOk, zLastOk, player->yRot, player->xRot); } } @@ -399,21 +380,24 @@ void PlayerConnection::teleport(double x, double y, double z, float yRot, void PlayerConnection::handlePlayerAction( std::shared_ptr packet) { ServerLevel* level = server->getLevel(player->dimension); + player->resetLastActionTime(); if (packet->action == PlayerActionPacket::DROP_ITEM) { - player->drop(); + player->drop(false); + return; + } else if (packet->action == PlayerActionPacket::DROP_ALL_ITEMS) { + player->drop(true); 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::ABORT_DESTROY_BLOCK) + shouldVerifyLocation = true; if (packet->action == PlayerActionPacket::STOP_DESTROY_BLOCK) shouldVerifyLocation = true; @@ -434,14 +418,15 @@ void PlayerConnection::handlePlayerAction( 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); + if (true) + player->gameMode->startDestroyBlock( + x, y, z, + packet->face); // 4J - condition was + // !server->isUnderSpawnProtection(level, + // x, y, z, player) (from Java 1.6.4) + // but putting back to old behaviour else player->connection->send(std::shared_ptr( new TileUpdatePacket(x, y, z, level))); @@ -461,19 +446,7 @@ void PlayerConnection::handlePlayerAction( if (level->getTile(x, y, z) != 0) player->connection->send(std::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(std::shared_ptr( - new TileUpdatePacket(x, y, z, level))); - } } - - // 4J Stu - Don't change the levels state - // level->canEditSpawn = false; } void PlayerConnection::handleUseItem(std::shared_ptr packet) { @@ -484,6 +457,7 @@ void PlayerConnection::handleUseItem(std::shared_ptr packet) { int y = packet->getY(); int z = packet->getZ(); int face = packet->getFace(); + player->resetLastActionTime(); // 4J Stu - We don't have ops, so just use the levels setting bool canEditSpawn = @@ -495,14 +469,13 @@ void PlayerConnection::handleUseItem(std::shared_ptr packet) { } 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) { + if (true) // 4J - condition was + // !server->isUnderSpawnProtection(level, x, y, z, + // player) (from java 1.6.4) but putting back to old + // behaviour + { player->gameMode->useItemOn( player, level, item, x, y, z, face, packet->getClickX(), packet->getClickY(), packet->getClickZ()); @@ -511,7 +484,7 @@ void PlayerConnection::handleUseItem(std::shared_ptr packet) { informClient = true; } else { - // player->connection->send(std::shared_ptr(new + // player->connection->send(shared_ptr(new // ChatPacket("\u00A77Height limit for building is " + // server->maxBuildHeight))); informClient = true; @@ -546,6 +519,11 @@ void PlayerConnection::handleUseItem(std::shared_ptr packet) { } item = player->inventory->getSelected(); + + bool forceClientUpdate = false; + if (item != NULL && packet->getItem() == NULL) { + forceClientUpdate = true; + } if (item != NULL && item->count == 0) { player->inventory->items[player->inventory->selected] = nullptr; item = nullptr; @@ -561,7 +539,8 @@ void PlayerConnection::handleUseItem(std::shared_ptr packet) { player->containerMenu->broadcastChanges(); player->ignoreSlotUpdateHack = false; - if (!ItemInstance::matches(player->inventory->getSelected(), + if (forceClientUpdate || + !ItemInstance::matches(player->inventory->getSelected(), packet->getItem())) { send(std::shared_ptr( new ContainerSetSlotPacket(player->containerMenu->containerId, @@ -569,9 +548,6 @@ void PlayerConnection::handleUseItem(std::shared_ptr packet) { player->inventory->getSelected()))); } } - - // 4J Stu - Don't change the levels state - // level->canEditSpawn = false; } void PlayerConnection::onDisconnect(DisconnectPacket::eDisconnectReason reason, @@ -641,6 +617,7 @@ void PlayerConnection::handleSetCarriedItem( return; } player->inventory->selected = packet->slot; + player->resetLastActionTime(); } void PlayerConnection::handleChat(std::shared_ptr packet) { @@ -686,6 +663,7 @@ void PlayerConnection::handleCommand(const std::wstring& message) { } void PlayerConnection::handleAnimate(std::shared_ptr packet) { + player->resetLastActionTime(); if (packet->action == AnimatePacket::SWING) { player->swing(); } @@ -693,6 +671,7 @@ void PlayerConnection::handleAnimate(std::shared_ptr packet) { void PlayerConnection::handlePlayerCommand( std::shared_ptr packet) { + player->resetLastActionTime(); if (packet->action == PlayerCommandPacket::START_SNEAKING) { player->setSneaking(true); } else if (packet->action == PlayerCommandPacket::STOP_SNEAKING) { @@ -704,6 +683,20 @@ void PlayerConnection::handlePlayerCommand( } else if (packet->action == PlayerCommandPacket::STOP_SLEEPING) { player->stopSleepInBed(false, true, true); synched = false; + } else if (packet->action == PlayerCommandPacket::RIDING_JUMP) { + // currently only supported by horses... + if ((player->riding != NULL) && + player->riding->GetType() == eTYPE_HORSE) { + std::dynamic_pointer_cast(player->riding) + ->onPlayerJump(packet->data); + } + } else if (packet->action == PlayerCommandPacket::OPEN_INVENTORY) { + // also only supported by horses... + if ((player->riding != NULL) && + player->riding->instanceof(eTYPE_HORSE)) { + std::dynamic_pointer_cast(player->riding) + ->openInventory(player); + } } else if (packet->action == PlayerCommandPacket::START_IDLEANIM) { player->setIsIdle(true); } else if (packet->action == PlayerCommandPacket::STOP_IDLEANIM) { @@ -737,11 +730,12 @@ void PlayerConnection::warn(const std::wstring& string) { // send( std::shared_ptr( new ChatPacket(L"ยง9" + string) ) ); } -std::wstring PlayerConnection::getConsoleName() { return player->name; } +std::wstring PlayerConnection::getConsoleName() { return player->getName(); } void PlayerConnection::handleInteract(std::shared_ptr packet) { ServerLevel* level = server->getLevel(player->dimension); std::shared_ptr target = level->getEntity(packet->target); + player->resetLastActionTime(); // 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 @@ -763,6 +757,14 @@ void PlayerConnection::handleInteract(std::shared_ptr packet) { if (packet->action == InteractPacket::INTERACT) { player->interact(target); } else if (packet->action == InteractPacket::ATTACK) { + if ((target->GetType() == eTYPE_ITEMENTITY) || + (target->GetType() == eTYPE_EXPERIENCEORB) || + (target->GetType() == eTYPE_ARROW) || target == player) { + // disconnect("Attempting to attack an invalid entity"); + // server.warn("Player " + player.getName() + " tried to attack + // an invalid entity"); + return; + } player->attack(target); } //} @@ -1033,6 +1035,33 @@ void PlayerConnection::handleServerSettingsChanged( app.SetGameHostOption( eGameHostOption_TNT, app.GetGameHostOption(packet->data, eGameHostOption_TNT)); + app.SetGameHostOption( + eGameHostOption_MobGriefing, + app.GetGameHostOption(packet->data, + eGameHostOption_MobGriefing)); + app.SetGameHostOption( + eGameHostOption_KeepInventory, + app.GetGameHostOption(packet->data, + eGameHostOption_KeepInventory)); + app.SetGameHostOption( + eGameHostOption_DoMobSpawning, + app.GetGameHostOption(packet->data, + eGameHostOption_DoMobSpawning)); + app.SetGameHostOption( + eGameHostOption_DoMobLoot, + app.GetGameHostOption(packet->data, eGameHostOption_DoMobLoot)); + app.SetGameHostOption( + eGameHostOption_DoTileDrops, + app.GetGameHostOption(packet->data, + eGameHostOption_DoTileDrops)); + app.SetGameHostOption( + eGameHostOption_DoDaylightCycle, + app.GetGameHostOption(packet->data, + eGameHostOption_DoDaylightCycle)); + app.SetGameHostOption( + eGameHostOption_NaturalRegeneration, + app.GetGameHostOption(packet->data, + eGameHostOption_NaturalRegeneration)); server->getPlayers()->broadcastAll( std::shared_ptr( @@ -1063,6 +1092,7 @@ void PlayerConnection::handleGameCommand( void PlayerConnection::handleClientCommand( std::shared_ptr packet) { + player->resetLastActionTime(); if (packet->action == ClientCommandPacket::PERFORM_RESPAWN) { if (player->wonGame) { player = server->getPlayers()->respawn( @@ -1072,10 +1102,10 @@ void PlayerConnection::handleClientCommand( // else if (player.getLevel().getLevelData().isHardcore()) //{ // if (server.isSingleplayer() && - //player.name.equals(server.getSingleplayerName())) + // player.name.equals(server.getSingleplayerName())) // { // player.connection.disconnect("You have died. Game over, - //man, it's game over!"); server.selfDestruct(); + // man, it's game over!"); server.selfDestruct(); // } // else // { @@ -1084,7 +1114,7 @@ void PlayerConnection::handleClientCommand( // server.getPlayers().getBans().add(ban); // player.connection.disconnect("You have died. Game over, - //man, it's game over!"); + // man, it's game over!"); // } //} else { @@ -1135,13 +1165,11 @@ void PlayerConnection::handleContainerSetSlot( void PlayerConnection::handleContainerClick( std::shared_ptr packet) { + player->resetLastActionTime(); if (player->containerMenu->containerId == packet->containerId && player->containerMenu->isSynched(player)) { std::shared_ptr clicked = player->containerMenu->clicked( - packet->slotNum, packet->buttonNum, - packet->quickKey ? AbstractContainerMenu::CLICK_QUICK_MOVE - : AbstractContainerMenu::CLICK_PICKUP, - player); + packet->slotNum, packet->buttonNum, packet->clickType, player); if (ItemInstance::matches(packet->item, clicked)) { // Yep, you sure did click what you claimed to click! @@ -1161,9 +1189,9 @@ void PlayerConnection::handleContainerClick( player->containerMenu->setSynched(player, false); std::vector > items; - for (unsigned int i = 0; i < player->containerMenu->slots->size(); + for (unsigned int i = 0; i < player->containerMenu->slots.size(); i++) { - items.push_back(player->containerMenu->slots->at(i)->getItem()); + items.push_back(player->containerMenu->slots.at(i)->getItem()); } player->refreshContainer(player->containerMenu, &items); @@ -1174,6 +1202,7 @@ void PlayerConnection::handleContainerClick( void PlayerConnection::handleContainerButtonClick( std::shared_ptr packet) { + player->resetLastActionTime(); if (player->containerMenu->containerId == packet->containerId && player->containerMenu->isSynched(player)) { player->containerMenu->clickMenuButton(player, packet->buttonId); @@ -1261,9 +1290,9 @@ void PlayerConnection::handleSetCreativeModeSlot( // as the client also incorrectly predicts the auxvalue of the // mapItem std::vector > items; - for (unsigned int i = 0; i < player->inventoryMenu->slots->size(); + for (unsigned int i = 0; i < player->inventoryMenu->slots.size(); i++) { - items.push_back(player->inventoryMenu->slots->at(i)->getItem()); + items.push_back(player->inventoryMenu->slots.at(i)->getItem()); } player->refreshContainer(player->inventoryMenu, &items); } @@ -1283,6 +1312,7 @@ void PlayerConnection::handleContainerAck( void PlayerConnection::handleSignUpdate( std::shared_ptr packet) { + player->resetLastActionTime(); app.DebugPrintf("PlayerConnection::handleSignUpdate\n"); ServerLevel* level = server->getLevel(player->dimension); @@ -1293,8 +1323,8 @@ void PlayerConnection::handleSignUpdate( if (std::dynamic_pointer_cast(te) != NULL) { std::shared_ptr ste = std::dynamic_pointer_cast(te); - if (!ste->isEditable()) { - server->warn(L"Player " + player->name + + if (!ste->isEditable() || ste->getPlayerWhoMayEdit() != player) { + server->warn(L"Player " + player->getName() + L" just tried to change non-editable sign"); return; } @@ -1552,7 +1582,7 @@ void PlayerConnection::handleCustomPayload( ItemInstance carried = player.inventory.getSelected(); if (sentItem != null && sentItem.id == Item.writingBook.id && sentItem.id == carried.id) { - carried.setTag(sentItem.getTag()); + carried.addTagElement(WrittenBookItem.TAG_PAGES, sentItem.getTag().getList(WrittenBookItem.TAG_PAGES)); } } else if (CustomPayloadPacket.CUSTOM_BOOK_SIGN_PACKET.equals(customPayloadPacket.identifier)) @@ -1569,7 +1599,9 @@ void PlayerConnection::handleCustomPayload( ItemInstance carried = player.inventory.getSelected(); if (sentItem != null && sentItem.id == Item.writtenBook.id && carried.id == Item.writingBook.id) { - carried.setTag(sentItem.getTag()); + carried.addTagElement(WrittenBookItem.TAG_AUTHOR, new StringTag(WrittenBookItem.TAG_AUTHOR, player.getName())); + carried.addTagElement(WrittenBookItem.TAG_TITLE, new StringTag(WrittenBookItem.TAG_TITLE, sentItem.getTag().getString(WrittenBookItem.TAG_TITLE))); + carried.addTagElement(WrittenBookItem.TAG_PAGES, sentItem.getTag().getList(WrittenBookItem.TAG_PAGES)); carried.id = Item.writtenBook.id; } } @@ -1585,9 +1617,55 @@ void PlayerConnection::handleCustomPayload( if (dynamic_cast(menu)) { ((MerchantMenu*)menu)->setSelectionHint(selection); } + } else if (CustomPayloadPacket::SET_ADVENTURE_COMMAND_PACKET.compare( + customPayloadPacket->identifier) == 0) { + if (!server->isCommandBlockEnabled()) { + app.DebugPrintf("Command blocks not enabled"); + // player->sendMessage(ChatMessageComponent.forTranslation("advMode.notEnabled")); + } else if (player->hasPermission(eGameCommand_Effect) && + player->abilities.instabuild) { + ByteArrayInputStream bais(customPayloadPacket->data); + DataInputStream input(&bais); + int x = input.readInt(); + int y = input.readInt(); + int z = input.readInt(); + std::wstring command = Packet::readUtf(&input, 256); + + std::shared_ptr tileEntity = + player->level->getTileEntity(x, y, z); + std::shared_ptr cbe = + std::dynamic_pointer_cast(tileEntity); + if (tileEntity != NULL && cbe != NULL) { + cbe->setCommand(command); + player->level->sendTileUpdated(x, y, z); + // player->sendMessage(ChatMessageComponent.forTranslation("advMode.setCommand.success", + // command)); + } + } else { + // player.sendMessage(ChatMessageComponent.forTranslation("advMode.notAllowed")); + } + } else if (CustomPayloadPacket::SET_BEACON_PACKET.compare( + customPayloadPacket->identifier) == 0) { + if (dynamic_cast(player->containerMenu) != NULL) { + ByteArrayInputStream bais(customPayloadPacket->data); + DataInputStream input(&bais); + int primary = input.readInt(); + int secondary = input.readInt(); + + BeaconMenu* beaconMenu = (BeaconMenu*)player->containerMenu; + Slot* slot = beaconMenu->getSlot(0); + if (slot->hasItem()) { + slot->remove(1); + std::shared_ptr beacon = + beaconMenu->getBeacon(); + beacon->setPrimaryPower(primary); + beacon->setSecondaryPower(secondary); + beacon->setChanged(); + } + } } else if (CustomPayloadPacket::SET_ITEM_NAME_PACKET.compare( customPayloadPacket->identifier) == 0) { - RepairMenu* menu = dynamic_cast(player->containerMenu); + AnvilMenu* menu = dynamic_cast(player->containerMenu); if (menu) { if (customPayloadPacket->data.data == NULL || customPayloadPacket->data.length < 1) { @@ -1604,6 +1682,8 @@ void PlayerConnection::handleCustomPayload( } } +bool PlayerConnection::isDisconnected() { return done; } + // 4J Added void PlayerConnection::handleDebugOptions( @@ -1634,6 +1714,10 @@ void PlayerConnection::handleCraftItem( // no room in inventory, so throw it down player->drop(pTempItemInst); } + } else if (pTempItemInst->id == Item::fireworksCharge_Id || + pTempItemInst->id == Item::fireworks_Id) { + CraftingMenu* menu = (CraftingMenu*)player->containerMenu; + player->openFireworks(menu->getX(), menu->getY(), menu->getZ()); } else { // TODO 4J Stu - Assume at the moment that the client can work this out // for us... @@ -1692,9 +1776,9 @@ void PlayerConnection::handleCraftItem( // as the client also incorrectly predicts the auxvalue of the // mapItem std::vector > items; - for (unsigned int i = 0; i < player->containerMenu->slots->size(); + for (unsigned int i = 0; i < player->containerMenu->slots.size(); i++) { - items.push_back(player->containerMenu->slots->at(i)->getItem()); + items.push_back(player->containerMenu->slots.at(i)->getItem()); } player->refreshContainer(player->containerMenu, &items); } else { diff --git a/Minecraft.Client/Network/PlayerConnection.h b/Minecraft.Client/Network/PlayerConnection.h index 16c0e90dc..cdc5f4e9f 100644 --- a/Minecraft.Client/Network/PlayerConnection.h +++ b/Minecraft.Client/Network/PlayerConnection.h @@ -28,9 +28,9 @@ private: bool didTick; int lastKeepAliveId; - __int64 lastKeepAliveTime; + int64_t lastKeepAliveTime; static Random random; - __int64 lastKeepAliveTick; + int64_t lastKeepAliveTick; int chatSpamTickCount; int dropSpamTickCount; @@ -108,6 +108,7 @@ public: std::shared_ptr playerAbilitiesPacket); virtual void handleCustomPayload( std::shared_ptr customPayloadPacket); + virtual bool isDisconnected(); // 4J Added virtual void handleCraftItem(std::shared_ptr packet); @@ -156,4 +157,4 @@ private: std::vector m_texturesRequested; bool m_bWasKicked; -}; +}; \ No newline at end of file diff --git a/Minecraft.Client/Network/PlayerList.cpp b/Minecraft.Client/Network/PlayerList.cpp index 9f32610d8..74cd3bf38 100644 --- a/Minecraft.Client/Network/PlayerList.cpp +++ b/Minecraft.Client/Network/PlayerList.cpp @@ -25,6 +25,7 @@ #include "../../Minecraft.World/Headers/net.minecraft.world.level.storage.h" #include "../../Minecraft.World/Headers/net.minecraft.world.level.saveddata.h" #include "../../Minecraft.World/Util/JavaMath.h" +#include "../../Minecraft.World/Level/Storage/EntityIO.h" #ifdef _XBOX #include "../Platform/Xbox/Network/NetworkPlayerXbox.h" #elif defined(__PS3__) || defined(__ORBIS__) @@ -76,7 +77,10 @@ PlayerList::~PlayerList() { void PlayerList::placeNewPlayer(Connection* connection, std::shared_ptr player, std::shared_ptr packet) { - bool newPlayer = load(player); + CompoundTag* playerTag = load(player); + + bool newPlayer = playerTag == NULL; + player->setLevel(server->getLevel(player->dimension)); player->gameMode->setLevel((ServerLevel*)player->level); @@ -240,19 +244,22 @@ void PlayerList::placeNewPlayer(Connection* connection, playerConnection->send(std::shared_ptr(new LoginPacket( L"", player->entityId, level->getLevelData()->getGenerator(), level->getSeed(), player->gameMode->getGameModeForPlayer()->getId(), - (std::uint8_t)level->dimension->id, - (std::uint8_t)level->getMaxBuildHeight(), (std::uint8_t)getMaxPlayers(), - level->difficulty, TelemetryManager->GetMultiplayerInstanceID(), - playerIndex, level->useNewSeaLevel(), - player->getAllPlayerGamePrivileges(), + (uint8_t)level->dimension->id, (uint8_t)level->getMaxBuildHeight(), + (uint8_t)getMaxPlayers(), level->difficulty, + TelemetryManager->GetMultiplayerInstanceID(), (BYTE)playerIndex, + level->useNewSeaLevel(), player->getAllPlayerGamePrivileges(), level->getLevelData()->getXZSize(), level->getLevelData()->getHellScale()))); playerConnection->send(std::shared_ptr( new SetSpawnPositionPacket(spawnPos->x, spawnPos->y, spawnPos->z))); playerConnection->send(std::shared_ptr( new PlayerAbilitiesPacket(&player->abilities))); + playerConnection->send(std::shared_ptr( + new SetCarriedItemPacket(player->inventory->selected))); delete spawnPos; + updateEntireScoreboard((ServerScoreboard*)level->getScoreboard(), player); + sendLevelInfo(player, level); // 4J-PB - removed, since it needs to be localised in the language the @@ -274,8 +281,9 @@ void PlayerList::placeNewPlayer(Connection* connection, player->xRot); server->getConnection()->addPlayerConnection(playerConnection); - playerConnection->send( - std::shared_ptr(new SetTimePacket(level->getTime()))); + playerConnection->send(std::shared_ptr(new SetTimePacket( + level->getGameTime(), level->getDayTime(), + level->getGameRules()->getBoolean(GameRules::RULE_DAYLIGHT)))); AUTO_VAR(activeEffects, player->getActiveEffects()); for (AUTO_VAR(it, activeEffects->begin()); it != activeEffects->end(); @@ -287,6 +295,18 @@ void PlayerList::placeNewPlayer(Connection* connection, player->initMenu(); + if (playerTag != NULL && playerTag->contains(Entity::RIDING_TAG)) { + // this player has been saved with a mount tag + std::shared_ptr mount = EntityIO::loadStatic( + playerTag->getCompound(Entity::RIDING_TAG), level); + if (mount != NULL) { + mount->forcedLoading = true; + level->addEntity(mount); + player->ride(mount); + mount->forcedLoading = false; + } + } + // If we are joining at the same time as someone in the end on this system // is travelling through the win portal, then we should set our wonGame flag // to true so that respawning works when the EndPoem is closed @@ -305,6 +325,35 @@ void PlayerList::placeNewPlayer(Connection* connection, } } +void PlayerList::updateEntireScoreboard(ServerScoreboard* scoreboardstd::, + std::shared_ptr player) { + // unordered_set objectives; + + // for (PlayerTeam team : scoreboard->getPlayerTeams()) + //{ + // player->connection->send( shared_ptr(new + // SetPlayerTeamPacket(team, SetPlayerTeamPacket::METHOD_ADD))); + // } + + // for (int slot = 0; slot < Scoreboard::DISPLAY_SLOTS; slot++) + //{ + // Objective objective = scoreboard->getDisplayObjective(slot); + + // if (objective != NULL && !objectives->contains(objective)) + // { + // vector > *packets = + // scoreboard->getStartTrackingPackets(objective); + + // for (Packet packet : packets) + // { + // player->connection->send(packet); + // } + + // objectives->add(objective); + // } + //} +} + void PlayerList::setLevel(ServerLevelArray levels) { playerIo = levels[0]->getLevelStorage()->getPlayerIO(); } @@ -323,8 +372,7 @@ int PlayerList::getMaxRange() { return PlayerChunkMap::convertChunkRangeToBlock(getViewDistance()); } -// 4J Changed return val to bool to check if new player or loaded player -bool PlayerList::load(std::shared_ptr player) { +CompoundTag* PlayerList::load(std::shared_ptr player) { return playerIo->load(player); } @@ -346,6 +394,8 @@ void PlayerList::validatePlayerSpawnPosition( app.DebugPrintf("Original pos is %f, %f, %f in dimension %d\n", player->x, player->y, player->z, player->dimension); + bool spawnForced = player->isRespawnForced(); + double targetX = 0; if (player->x < 0) targetX = Mth::ceil(player->x) - 0.5; @@ -397,12 +447,12 @@ void PlayerList::validatePlayerSpawnPosition( Pos* bedPosition = player->getRespawnPosition(); if (bedPosition != NULL) { Pos* respawnPosition = Player::checkBedValidRespawnPosition( - server->getLevel(player->dimension), bedPosition); + server->getLevel(player->dimension), bedPosition, spawnForced); if (respawnPosition != NULL) { player->moveTo(respawnPosition->x + 0.5f, respawnPosition->y + 0.1f, respawnPosition->z + 0.5f, 0, 0); - player->setRespawnPosition(bedPosition); + player->setRespawnPosition(bedPosition, spawnForced); } delete bedPosition; } @@ -480,6 +530,13 @@ void PlayerList::remove(std::shared_ptr player) { // sure that the player is gone delete the map if (player->isGuest()) playerIo->deleteMapFilesForPlayer(player); ServerLevel* level = player->getLevel(); + if (player->riding != NULL) { + // remove mount first because the player unmounts when being + // removed, also remove mount because it's saved in the player's + // save tag + level->removeEntityImmediately(player->riding); + app.DebugPrintf("removing player mount"); + } level->removeEntity(player); level->getChunkMap()->remove(player); AUTO_VAR(it, find(players.begin(), players.end(), player)); @@ -601,6 +658,7 @@ std::shared_ptr PlayerList::respawn( ->removeEntityImmediately(serverPlayer); Pos* bedPosition = serverPlayer->getRespawnPosition(); + bool spawnForced = serverPlayer->isRespawnForced(); removePlayerFromReceiving(serverPlayer); serverPlayer->dimension = targetDimension; @@ -613,9 +671,10 @@ std::shared_ptr PlayerList::respawn( std::shared_ptr player = std::shared_ptr( new ServerPlayer(server, server->getLevel(serverPlayer->dimension), - serverPlayer->name, + serverPlayer->getName(), new ServerPlayerGameMode( server->getLevel(serverPlayer->dimension)))); + player->connection = serverPlayer->connection; player->restoreFrom(serverPlayer, keepAllPlayerData); if (keepAllPlayerData) { // Fix for #81759 - TU9: Content: Gameplay: Entering The End Exit Portal @@ -633,7 +692,6 @@ std::shared_ptr PlayerList::respawn( // packet // player->entityId = serverPlayer->entityId; - player->connection = serverPlayer->connection; player->setPlayerDefaultSkin(skin); player->setIsGuest(serverPlayer->isGuest()); player->setPlayerIndex(playerIndex); @@ -665,7 +723,7 @@ std::shared_ptr PlayerList::respawn( player->moveTo(serverPlayer->x, serverPlayer->y, serverPlayer->z, serverPlayer->yRot, serverPlayer->xRot); if (bedPosition != NULL) { - player->setRespawnPosition(bedPosition); + player->setRespawnPosition(bedPosition, spawnForced); delete bedPosition; } // Fix for #81759 - TU9: Content: Gameplay: Entering The End Exit Portal @@ -674,11 +732,12 @@ std::shared_ptr PlayerList::respawn( player->inventory->selected = serverPlayer->inventory->selected; } else if (bedPosition != NULL) { Pos* respawnPosition = Player::checkBedValidRespawnPosition( - server->getLevel(serverPlayer->dimension), bedPosition); + server->getLevel(serverPlayer->dimension), bedPosition, + spawnForced); if (respawnPosition != NULL) { player->moveTo(respawnPosition->x + 0.5f, respawnPosition->y + 0.1f, respawnPosition->z + 0.5f, 0, 0); - player->setRespawnPosition(bedPosition); + player->setRespawnPosition(bedPosition, spawnForced); } else { player->connection->send( std::shared_ptr(new GameEventPacket( @@ -703,6 +762,10 @@ std::shared_ptr PlayerList::respawn( level->getLevelData()->getHellScale()))); player->connection->teleport(player->x, player->y, player->z, player->yRot, player->xRot); + player->connection->send( + std::shared_ptr(new SetExperiencePacket( + player->experienceProgress, player->totalExperience, + player->experienceLevel))); if (keepAllPlayerData) { std::vector* activeEffects = @@ -725,6 +788,7 @@ std::shared_ptr PlayerList::respawn( players.push_back(player); player->initMenu(); + player->setHealth(player->getHealth()); // 4J-JEV - Dying before this point in the tutorial is pretty annoying, // making sure to remove health/hunger and give you back your meat. @@ -772,9 +836,9 @@ void PlayerList::toggleDimension(std::shared_ptr player, // (1) if this isn't the primary player, then we just need to remove it from // the entity tracker (2) if this Is the primary player then: // (a) if isEmptying is true, then remove the player from the - //tracker, and send "remove entity" packets for anything seen (this is the - //original behaviour of the code) (b) if isEmptying is false, then we'll be - //transferring control of entity tracking to another player + // tracker, and send "remove entity" packets for anything seen (this is the + // original behaviour of the code) (b) if isEmptying is false, then + // we'll be transferring control of entity tracking to another player if (isPrimary) { if (isEmptying) { @@ -824,58 +888,7 @@ void PlayerList::toggleDimension(std::shared_ptr player, oldLevel->removeEntityImmediately(player); player->removed = false; - double xt = player->x; - double zt = player->z; - double scale = - newLevel->getLevelData() - ->getHellScale(); // 4J Scale was 8 but this is all we can fit in - if (player->dimension == -1) { - xt /= scale; - zt /= scale; - player->moveTo(xt, player->y, zt, player->yRot, player->xRot); - if (player->isAlive()) { - oldLevel->tick(player, false); - } - } else if (player->dimension == 0) { - xt *= scale; - zt *= scale; - player->moveTo(xt, player->y, zt, player->yRot, player->xRot); - if (player->isAlive()) { - oldLevel->tick(player, false); - } - } else { - Pos* p = newLevel->getDimensionSpecificSpawn(); - - xt = p->x; - player->y = p->y; - zt = p->z; - delete p; - player->moveTo(xt, player->y, zt, 90, 0); - if (player->isAlive()) { - oldLevel->tick(player, false); - } - } - - removePlayerFromReceiving(player, false, lastDimension); - addPlayerToReceiving(player); - - if (lastDimension == 1) { - } else { - xt = (double)Mth::clamp((int)xt, -Level::MAX_LEVEL_SIZE + 128, - Level::MAX_LEVEL_SIZE - 128); - zt = (double)Mth::clamp((int)zt, -Level::MAX_LEVEL_SIZE + 128, - Level::MAX_LEVEL_SIZE - 128); - if (player->isAlive()) { - newLevel->addEntity(player); - player->moveTo(xt, player->y, zt, player->yRot, player->xRot); - newLevel->tick(player, false); - newLevel->cache->autoCreate = true; - (new PortalForcer())->force(newLevel, player); - newLevel->cache->autoCreate = false; - } - } - - player->setLevel(newLevel); + repositionAcrossDimension(player, lastDimension, oldLevel, newLevel); changeDimension(player, oldLevel); player->gameMode->setLevel(newLevel); @@ -912,6 +925,80 @@ void PlayerList::toggleDimension(std::shared_ptr player, sendAllPlayerInfo(player); } +void PlayerList::repositionAcrossDimension(std::shared_ptr entity, + int lastDimension, + ServerLevel* oldLevel, + ServerLevel* newLevel) { + double xt = entity->x; + double zt = entity->z; + double xOriginal = entity->x; + double yOriginal = entity->y; + double zOriginal = entity->z; + float yRotOriginal = entity->yRot; + double scale = + newLevel->getLevelData() + ->getHellScale(); // 4J Scale was 8 but this is all we can fit in + if (entity->dimension == -1) { + xt /= scale; + zt /= scale; + entity->moveTo(xt, entity->y, zt, entity->yRot, entity->xRot); + if (entity->isAlive()) { + oldLevel->tick(entity, false); + } + } else if (entity->dimension == 0) { + xt *= scale; + zt *= scale; + entity->moveTo(xt, entity->y, zt, entity->yRot, entity->xRot); + if (entity->isAlive()) { + oldLevel->tick(entity, false); + } + } else { + Pos* p; + + if (lastDimension == 1) { + // Coming from the end + p = newLevel->getSharedSpawnPos(); + } else { + // Going to the end + p = newLevel->getDimensionSpecificSpawn(); + } + + xt = p->x; + entity->y = p->y; + zt = p->z; + delete p; + entity->moveTo(xt, entity->y, zt, 90, 0); + if (entity->isAlive()) { + oldLevel->tick(entity, false); + } + } + + if (entity->GetType() == eTYPE_SERVERPLAYER) { + std::shared_ptr player = + std::dynamic_pointer_cast(entity); + removePlayerFromReceiving(player, false, lastDimension); + addPlayerToReceiving(player); + } + + if (lastDimension != 1) { + xt = (double)Mth::clamp((int)xt, -Level::MAX_LEVEL_SIZE + 128, + Level::MAX_LEVEL_SIZE - 128); + zt = (double)Mth::clamp((int)zt, -Level::MAX_LEVEL_SIZE + 128, + Level::MAX_LEVEL_SIZE - 128); + if (entity->isAlive()) { + newLevel->addEntity(entity); + entity->moveTo(xt, entity->y, zt, entity->yRot, entity->xRot); + newLevel->tick(entity, false); + newLevel->cache->autoCreate = true; + newLevel->getPortalForcer()->force(entity, xOriginal, yOriginal, + zOriginal, yRotOriginal); + newLevel->cache->autoCreate = false; + } + } + + entity->setLevel(newLevel); +} + void PlayerList::tick() { // 4J - brought changes to how often this is sent forward from 1.2.3 if (++sendAllPlayerInfoIn > SEND_PLAYER_INFO_INTERVAL) { @@ -1095,6 +1182,121 @@ std::shared_ptr PlayerList::getPlayer(PlayerUID uid) { return nullptr; } +std::shared_ptr PlayerList::getNearestPlayer(Pos* position, + int range) { + if (players.empty()) return nullptr; + if (position == NULL) return players.at(0); + std::shared_ptr current = nullptr; + double dist = -1; + int rangeSqr = range * range; + + for (int i = 0; i < players.size(); i++) { + std::shared_ptr next = players.at(i); + double newDist = + position->distSqr(next->getCommandSenderWorldPosition()); + + if ((dist == -1 || newDist < dist) && + (range <= 0 || newDist <= rangeSqr)) { + dist = newDist; + current = next; + } + } + + return current; +} + +std::vector* PlayerList::getPlayers( + Pos* position, int rangeMin, int rangeMax, int count, int mode, + int levelMin, int levelMax, + std::unordered_map* scoreRequirements, + const std::wstring& playerName, const std::wstring& teamName, + Level* level) { + app.DebugPrintf("getPlayers NOT IMPLEMENTED!"); + return NULL; + + /*if (players.empty()) return NULL; + vector > result = new + vector >(); bool reverse = count < 0; bool + playerNameNot = !playerName.empty() && playerName.startsWith("!"); bool + teamNameNot = !teamName.empty() && teamName.startsWith("!"); int rangeMinSqr + = rangeMin * rangeMin; int rangeMaxSqr = rangeMax * rangeMax; count = + Mth.abs(count); + + if (playerNameNot) playerName = playerName.substring(1); + if (teamNameNot) teamName = teamName.substring(1); + + for (int i = 0; i < players.size(); i++) { + ServerPlayer player = players.get(i); + + if (level != null && player.level != level) continue; + if (playerName != null) { + if (playerNameNot == playerName.equalsIgnoreCase(player.getAName())) + continue; + } + if (teamName != null) { + Team team = player.getTeam(); + String actualName = team == null ? "" : team.getName(); + if (teamNameNot == teamName.equalsIgnoreCase(actualName)) continue; + } + + if (position != null && (rangeMin > 0 || rangeMax > 0)) { + float distance = position.distSqr(player.getCommandSenderWorldPosition()); + if (rangeMin > 0 && distance < rangeMinSqr) continue; + if (rangeMax > 0 && distance > rangeMaxSqr) continue; + } + + if (!meetsScoreRequirements(player, scoreRequirements)) continue; + + if (mode != GameType.NOT_SET.getId() && mode != + player.gameMode.getGameModeForPlayer().getId()) continue; if (levelMin > 0 + && player.experienceLevel < levelMin) continue; if (player.experienceLevel > + levelMax) continue; + + result.add(player); + } + + if (position != null) Collections.sort(result, new + PlayerDistanceComparator(position)); if (reverse) + Collections.reverse(result); if (count > 0) result = result.subList(0, + Math.min(count, result.size())); + + return result;*/ +} + +bool PlayerList::meetsScoreRequirements( + std::shared_ptr player, + std::unordered_map scoreRequirements) { + app.DebugPrintf("meetsScoreRequirements NOT IMPLEMENTED!"); + return false; + + // if (scoreRequirements == null || scoreRequirements.size() == 0) return + // true; + + // for (Map.Entry requirement : + // scoreRequirements.entrySet()) { String name = requirement.getKey(); + // boolean min = false; + + // if (name.endsWith("_min") && name.length() > 4) { + // min = true; + // name = name.substring(0, name.length() - 4); + // } + + // Scoreboard scoreboard = player.getScoreboard(); + // Objective objective = scoreboard.getObjective(name); + // if (objective == null) return false; + // Score score = player.getScoreboard().getPlayerScore(player.getAName(), + // objective); int value = score.getScore(); + + // if (value < requirement.getValue() && min) { + // return false; + // } else if (value > requirement.getValue() && !min) { + // return false; + // } + //} + + // return true; +} + void PlayerList::sendMessage(const std::wstring& name, const std::wstring& message) { std::shared_ptr player = getPlayer(name); @@ -1168,27 +1370,6 @@ void PlayerList::broadcast(std::shared_ptr except, double x, double y, } } -void PlayerList::broadcastToAllOps(const std::wstring& message) { - std::shared_ptr chatPacket = - std::shared_ptr(new ChatPacket(message)); - for (unsigned int i = 0; i < players.size(); i++) { - std::shared_ptr p = players[i]; - if (isOp(p->name)) { - p->connection->send(chatPacket); - } - } -} - -bool PlayerList::sendTo(const std::wstring& name, - std::shared_ptr packet) { - std::shared_ptr player = getPlayer(name); - if (player != NULL) { - player->connection->send(packet); - return true; - } - return false; -} - void PlayerList::saveAll(ProgressListener* progressListener, bool bDeleteGuestMaps /*= false*/) { if (progressListener != NULL) @@ -1222,8 +1403,9 @@ void PlayerList::reloadWhitelist() {} void PlayerList::sendLevelInfo(std::shared_ptr player, ServerLevel* level) { - player->connection->send( - std::shared_ptr(new SetTimePacket(level->getTime()))); + player->connection->send(std::shared_ptr(new SetTimePacket( + level->getGameTime(), level->getDayTime(), + level->getGameRules()->getBoolean(GameRules::RULE_DAYLIGHT)))); if (level->isRaining()) { player->connection->send(std::shared_ptr( new GameEventPacket(GameEventPacket::START_RAINING, 0))); @@ -1247,6 +1429,8 @@ void PlayerList::sendLevelInfo(std::shared_ptr player, void PlayerList::sendAllPlayerInfo(std::shared_ptr player) { player->refreshContainer(player->inventoryMenu); player->resetSentInfo(); + player->connection->send(std::shared_ptr( + new SetCarriedItemPacket(player->inventory->selected))); } int PlayerList::getPlayerCount() { return (int)players.size(); } @@ -1268,7 +1452,7 @@ MinecraftServer* PlayerList::getServer() { return server; } int PlayerList::getViewDistance() { return viewDistance; } void PlayerList::setOverrideGameMode(GameType* gameMode) { - this->overrideGameMode = gameMode; + overrideGameMode = gameMode; } void PlayerList::updatePlayerGameMode(std::shared_ptr newPlayer, diff --git a/Minecraft.Client/Network/PlayerList.h b/Minecraft.Client/Network/PlayerList.h index d810bcebe..b430128a9 100644 --- a/Minecraft.Client/Network/PlayerList.h +++ b/Minecraft.Client/Network/PlayerList.h @@ -14,6 +14,7 @@ class TileEntity; class ProgressListener; class GameType; class LoginPacket; +class ServerScoreboard; class PlayerList { private: @@ -70,13 +71,18 @@ public: void placeNewPlayer(Connection* connection, std::shared_ptr player, std::shared_ptr packet); + +protected: + void updateEntireScoreboard(ServerScoreboard* scoreboard, + std::shared_ptr player); + +public: void setLevel(ServerLevelArray levels); void changeDimension(std::shared_ptr player, ServerLevel* from); int getMaxRange(); - bool load(std::shared_ptr - player); // 4J Changed return val to bool to check if new - // player or loaded player + CompoundTag* load(std::shared_ptr player); + protected: void save(std::shared_ptr player); @@ -94,6 +100,9 @@ public: bool keepAllPlayerData); void toggleDimension(std::shared_ptr player, int targetDimension); + void repositionAcrossDimension(std::shared_ptr entity, + int lastDimension, ServerLevel* oldLevel, + ServerLevel* newLevel); void tick(); bool isTrackingTile(int x, int y, int z, int dimension); // 4J added void prioritiseTileChanges(int x, int y, int z, int dimension); // 4J added @@ -108,13 +117,25 @@ public: bool isOp(std::shared_ptr player); // 4J Added std::shared_ptr getPlayer(const std::wstring& name); std::shared_ptr getPlayer(PlayerUID uid); + std::shared_ptr getNearestPlayer(Pos* position, int range); + std::vector* getPlayers( + Pos* position, int rangeMin, int rangeMax, int count, int mode, + int levelMin, int levelMax, + std::unordered_map* scoreRequirements, + const std::wstring& playerName, const std::wstring& teamName, + Level* level); + +private: + bool meetsScoreRequirements( + std::shared_ptr player, + std::unordered_map scoreRequirements); + +public: void sendMessage(const std::wstring& name, const std::wstring& message); void broadcast(double x, double y, double z, double range, int dimension, std::shared_ptr packet); void broadcast(std::shared_ptr except, double x, double y, double z, double range, int dimension, std::shared_ptr packet); - void broadcastToAllOps(const std::wstring& message); - bool sendTo(const std::wstring& name, std::shared_ptr packet); // 4J Added ProgressListener *progressListener param and bDeleteGuestMaps // param void saveAll(ProgressListener* progressListener, diff --git a/Minecraft.Client/Network/ServerChunkCache.cpp b/Minecraft.Client/Network/ServerChunkCache.cpp index 09d4f6ce3..f2cfe1ba3 100644 --- a/Minecraft.Client/Network/ServerChunkCache.cpp +++ b/Minecraft.Client/Network/ServerChunkCache.cpp @@ -11,6 +11,7 @@ #include "../../Minecraft.World/Util/ThreadName.h" #include "../../Minecraft.World/IO/Streams/Compression.h" #include "../../Minecraft.World/Level/Storage/OldChunkStorage.h" +#include "../../Minecraft.World/Blocks/Tile.h" ServerChunkCache::ServerChunkCache(ServerLevel* level, ChunkStorage* storage, ChunkSource* source) { @@ -40,6 +41,7 @@ ServerChunkCache::ServerChunkCache(ServerLevel* level, ChunkStorage* storage, // 4J-PB added ServerChunkCache::~ServerChunkCache() { + storage->WaitForAll(); // MGH - added to fix crash bug 175183 delete emptyChunk; delete cache; delete source; @@ -336,6 +338,76 @@ LevelChunk* ServerChunkCache::getChunkLoadedOrUnloaded(int x, int z) { } #endif +// 4J MGH added, for expanding worlds, to kill any player changes and reset the +// chunk +#ifdef _LARGE_WORLDS +void ServerChunkCache::overwriteLevelChunkFromSource(int x, int z) { + int ix = x + XZOFFSET; + int iz = z + XZOFFSET; + // Check we're in range of the stored level + if ((ix < 0) || (ix >= XZSIZE)) assert(0); + if ((iz < 0) || (iz >= XZSIZE)) assert(0); + int idx = ix * XZSIZE + iz; + + LevelChunk* chunk = NULL; + chunk = source->getChunk(x, z); + assert(chunk); + if (chunk) { + save(chunk); + } +} + +void ServerChunkCache::updateOverwriteHellChunk(LevelChunk* origChunk, + LevelChunk* playerChunk, + int xMin, int xMax, int zMin, + int zMax) { + // replace a section of the chunk with the original source data, if it + // hasn't already changed + for (int x = xMin; x < xMax; x++) { + for (int z = zMin; z < zMax; z++) { + for (int y = 0; y < 256; y++) { + int playerTile = playerChunk->getTile(x, y, z); + if (playerTile == + Tile::unbreakable_Id) // if the tile is still unbreakable, + // the player hasn't changed it, so + // we can replace with the source + playerChunk->setTileAndData(x, y, z, + origChunk->getTile(x, y, z), + origChunk->getData(x, y, z)); + } + } + } +} + +void ServerChunkCache::overwriteHellLevelChunkFromSource(int x, int z, + int minVal, + int maxVal) { + int ix = x + XZOFFSET; + int iz = z + XZOFFSET; + // Check we're in range of the stored level + if ((ix < 0) || (ix >= XZSIZE)) assert(0); + if ((iz < 0) || (iz >= XZSIZE)) assert(0); + int idx = ix * XZSIZE + iz; + autoCreate = true; + LevelChunk* playerChunk = getChunk(x, z); + autoCreate = false; + LevelChunk* origChunk = source->getChunk(x, z); + assert(origChunk); + if (playerChunk != emptyChunk) { + if (x == minVal) + updateOverwriteHellChunk(origChunk, playerChunk, 0, 4, 0, 16); + if (x == maxVal) + updateOverwriteHellChunk(origChunk, playerChunk, 12, 16, 0, 16); + if (z == minVal) + updateOverwriteHellChunk(origChunk, playerChunk, 0, 16, 0, 4); + if (z == maxVal) + updateOverwriteHellChunk(origChunk, playerChunk, 0, 16, 12, 16); + } + save(playerChunk); +} + +#endif + // 4J Added // #ifdef _LARGE_WORLDS void ServerChunkCache::dontDrop(int x, int z) { @@ -362,7 +434,7 @@ LevelChunk* ServerChunkCache::load(int x, int z) { levelChunk = storage->load(level, x, z); } if (levelChunk != NULL) { - levelChunk->lastSaveTime = level->getTime(); + levelChunk->lastSaveTime = level->getGameTime(); } return levelChunk; } @@ -376,7 +448,7 @@ void ServerChunkCache::saveEntities(LevelChunk* levelChunk) { void ServerChunkCache::save(LevelChunk* levelChunk) { if (storage == NULL) return; - levelChunk->lastSaveTime = level->getTime(); + levelChunk->lastSaveTime = level->getGameTime(); storage->save(level, levelChunk); } @@ -710,6 +782,7 @@ bool ServerChunkCache::save(bool force, ProgressListener* progressListener) { notificationEvent[3]; // These are signalled by the threads to let // us know they are complete C4JThread* saveThreads[3]; + DWORD threadId[3]; SaveThreadData threadData[3]; ZeroMemory(&threadData[0], sizeof(SaveThreadData)); ZeroMemory(&threadData[1], sizeof(SaveThreadData)); @@ -787,7 +860,7 @@ bool ServerChunkCache::save(bool force, ProgressListener* progressListener) { if (saveThreads[j] == NULL) { char threadName[256]; sprintf(threadName, "Save thread %d\n", j); - SetThreadName(0, threadName); + SetThreadName(threadId[j], threadName); // saveThreads[j] = // CreateThread(NULL,0,runSaveThreadProc,&threadData[j],CREATE_SUSPENDED,&threadId[j]); @@ -926,22 +999,37 @@ bool ServerChunkCache::tick() { if (!m_toDrop.empty()) { LevelChunk* chunk = m_toDrop.front(); if (!chunk->isUnloaded()) { - save(chunk); - saveEntities(chunk); - chunk->unload(true); + // Don't unload a chunk that contains a player, as this will + // cause their entity to be removed from the level itself + // and they will never tick again. This can happen if a + // player moves a long distance in one tick, for example + // when the server thread has locked up doing something for + // a while whilst a player kept moving. In this case, the + // player is moved in the player chunk map (driven by the + // network packets being processed for their new position) + // before the player's tick is called to remove them from + // the chunk they used to be in, and add them to their + // current chunk. This will only be a temporary state and we + // should be able to unload the chunk on the next call to + // this tick. + if (!chunk->containsPlayer()) { + save(chunk); + saveEntities(chunk); + chunk->unload(true); - // loadedChunks.remove(cp); - // loadedChunkList.remove(chunk); - AUTO_VAR(it, std::find(m_loadedChunkList.begin(), - m_loadedChunkList.end(), chunk)); - if (it != m_loadedChunkList.end()) - m_loadedChunkList.erase(it); + // loadedChunks.remove(cp); + // loadedChunkList.remove(chunk); + AUTO_VAR(it, find(m_loadedChunkList.begin(), + m_loadedChunkList.end(), chunk)); + if (it != m_loadedChunkList.end()) + m_loadedChunkList.erase(it); - int ix = chunk->x + XZOFFSET; - int iz = chunk->z + XZOFFSET; - int idx = ix * XZSIZE + iz; - m_unloadedCache[idx] = chunk; - cache[idx] = NULL; + int ix = chunk->x + XZOFFSET; + int iz = chunk->z + XZOFFSET; + int idx = ix * XZSIZE + iz; + m_unloadedCache[idx] = chunk; + cache[idx] = NULL; + } } m_toDrop.pop_front(); } @@ -970,8 +1058,11 @@ TilePos* ServerChunkCache::findNearestMapFeature( return source->findNearestMapFeature(level, featureName, x, y, z); } -int ServerChunkCache::runSaveThreadProc(void* lpParam) { - SaveThreadData* params = static_cast(lpParam); +void ServerChunkCache::recreateLogicStructuresForChunk(int chunkX, int chunkZ) { +} + +int ServerChunkCache::runSaveThreadProc(LPVOID lpParam) { + SaveThreadData* params = (SaveThreadData*)lpParam; if (params->useSharedThreadStorage) { Compression::UseDefaultThreadStorage(); diff --git a/Minecraft.Client/Network/ServerChunkCache.h b/Minecraft.Client/Network/ServerChunkCache.h index c086728a0..abd7cb4b7 100644 --- a/Minecraft.Client/Network/ServerChunkCache.h +++ b/Minecraft.Client/Network/ServerChunkCache.h @@ -10,7 +10,7 @@ class ServerLevel; class ServerChunkCache : public ChunkSource { private: - // std::unordered_set toDrop; + // unordered_set toDrop; private: LevelChunk* emptyChunk; ChunkSource* source; @@ -48,6 +48,17 @@ public: virtual LevelChunk* getChunk(int x, int z); #ifdef _LARGE_WORLDS LevelChunk* getChunkLoadedOrUnloaded(int x, int z); // 4J added + void overwriteLevelChunkFromSource( + int x, int z); // 4J MGH added, for expanding worlds, to kill any + // player changes and reset the chunk + void overwriteHellLevelChunkFromSource( + int x, int z, int minVal, + int maxVal); // 4J MGH added, for expanding worlds, to reset the outer + // tiles in the chunk + void updateOverwriteHellChunk(LevelChunk* origChunk, + LevelChunk* playerChunk, int xMin, int xMax, + int zMin, int zMax); + #endif virtual LevelChunk** getCache() { return cache; } // 4J added @@ -89,6 +100,7 @@ public: virtual TilePos* findNearestMapFeature(Level* level, const std::wstring& featureName, int x, int y, int z); + virtual void recreateLogicStructuresForChunk(int chunkX, int chunkZ); private: typedef struct _SaveThreadData { diff --git a/Minecraft.Client/Network/ServerConnection.cpp b/Minecraft.Client/Network/ServerConnection.cpp index 65801536b..18dc87556 100644 --- a/Minecraft.Client/Network/ServerConnection.cpp +++ b/Minecraft.Client/Network/ServerConnection.cpp @@ -88,14 +88,10 @@ void ServerConnection::tick() { std::shared_ptr player = players[i]; std::shared_ptr serverPlayer = player->getPlayer(); if (serverPlayer) { + serverPlayer->updateFrameTick(); serverPlayer->doChunkSendingTick(false); } - // try { // 4J - removed try/catch player->tick(); - // } catch (Exception e) { - // logger.log(Level.WARNING, "Failed to handle packet: " + e, - // e); player.disconnect("Internal server error"); - // } if (player->done) { players.erase(players.begin() + i); i--; @@ -187,9 +183,14 @@ void ServerConnection::handleServerSettingsChanged( // // for (unsigned int i = 0; i < players.size(); i++) // { - // std::shared_ptr playerconnection = + // shared_ptr playerconnection = // players[i]; // playerconnection->setShowOnMaps(pMinecraft->options->GetGamertagSetting()); // } // } +} + +std::vector >* +ServerConnection::getPlayers() { + return &players; } \ No newline at end of file diff --git a/Minecraft.Client/Network/ServerConnection.h b/Minecraft.Client/Network/ServerConnection.h index 77a341df9..68ab89448 100644 --- a/Minecraft.Client/Network/ServerConnection.h +++ b/Minecraft.Client/Network/ServerConnection.h @@ -53,4 +53,5 @@ public: void handleTextureAndGeometryReceived(const std::wstring& textureName); void handleServerSettingsChanged( std::shared_ptr packet); + std::vector >* getPlayers(); }; diff --git a/Minecraft.Client/Network/ServerScoreboard.cpp b/Minecraft.Client/Network/ServerScoreboard.cpp new file mode 100644 index 000000000..f1eb63ec6 --- /dev/null +++ b/Minecraft.Client/Network/ServerScoreboard.cpp @@ -0,0 +1,233 @@ +#include "../Platform/stdafx.h" + +#include "ServerScoreboard.h" + +ServerScoreboard::ServerScoreboard(MinecraftServer* server) { + this->server = server; +} + +MinecraftServer* ServerScoreboard::getServer() { return server; } + +void ServerScoreboard::onScoreChanged(Score* score) { + // Scoreboard::onScoreChanged(score); + + // if (trackedObjectives.contains(score.getObjective())) + //{ + // server->getPlayers()->broadcastAll( shared_ptr( new + // SetScorePacket(score, SetScorePacket::METHOD_CHANGE))); + // } + + // setDirty(); +} + +void ServerScoreboard::onPlayerRemoved(const std::wstring& player) { + // Scoreboard::onPlayerRemoved(player); + // server->getPlayers()->broadcastAll( shared_ptr( new + // SetScorePacket(player))); setDirty(); +} + +void ServerScoreboard::setDisplayObjective(int slot, Objective* objective) { + // Objective *old = getDisplayObjective(slot); + + // Scoreboard::setDisplayObjective(slot, objective); + + // if (old != objective && old != NULL) + //{ + // if (getObjectiveDisplaySlotCount(old) > 0) + // { + // server->getPlayers()->broadcastAll( + // shared_ptr( new + // SetDisplayObjectivePacket(slot, objective))); + // } + // else + // { + // stopTrackingObjective(old); + // } + // } + + // if (objective != NULL) + //{ + // if (trackedObjectives.contains(objective)) + // { + // server->getPlayers()->broadcastAll( + // shared_ptr( new + // SetDisplayObjectivePacket(slot, objective))); + // } + // else + // { + // startTrackingObjective(objective); + // } + // } + + // setDirty(); +} + +void ServerScoreboard::addPlayerToTeam(const std::wstring& player, + PlayerTeam* team) { + // Scoreboard::addPlayerToTeam(player, team); + + // server->getPlayers()->broadcastAll( shared_ptr( new + // SetPlayerTeamPacket(team, Arrays::asList(player), + // SetPlayerTeamPacket::METHOD_JOIN))); + + // setDirty(); +} + +void ServerScoreboard::removePlayerFromTeam(const std::wstring& player, + PlayerTeam* team) { + // Scoreboard::removePlayerFromTeam(player, team); + + // server->getPlayers()->broadcastAll( shared_ptr( new + // SetPlayerTeamPacket(team, Arrays::asList(player), + // SetPlayerTeamPacket::METHOD_LEAVE))); + + // setDirty(); +} + +void ServerScoreboard::onObjectiveAdded(Objective* objective) { + // Scoreboard::onObjectiveAdded(objective); + // setDirty(); +} + +void ServerScoreboard::onObjectiveChanged(Objective* objective) { + // Scoreaboard::onObjectiveChanged(objective); + + // if (trackedObjectives.contains(objective)) + //{ + // server->getPlayers()->broadcastAll( shared_ptr( new + // SetObjectivePacket(objective, SetObjectivePacket::METHOD_CHANGE))); + // } + + // setDirty(); +} + +void ServerScoreboard::onObjectiveRemoved(Objective* objective) { + // Scoreboard::onObjectiveRemoved(objective); + + // if (trackedObjectives.contains(objective)) + //{ + // stopTrackingObjective(objective); + // } + + // setDirty(); +} + +void ServerScoreboard::onTeamAdded(PlayerTeam* team) { + // Scoreboard::onTeamAdded(team); + + // server->getPlayers()->broadcastAll( shared_ptr( new + // SetPlayerTeamPacket(team, SetPlayerTeamPacket::METHOD_ADD)) ); + + // setDirty(); +} + +void ServerScoreboard::onTeamChanged(PlayerTeam* team) { + // Scoreboard::onTeamChanged(team); + + // server->getPlayers()->broadcastAll( shared_ptr( new + // SetPlayerTeamPacket(team, SetPlayerTeamPacket::METHOD_CHANGE))); + + // setDirty(); +} + +void ServerScoreboard::onTeamRemoved(PlayerTeam* team) { + // Scoreboard::onTeamRemoved(team); + + // server->getPlayers()->broadcastAll( shared_ptr( new + // SetPlayerTeamPacket(team, SetPlayerTeamPacket::METHOD_REMOVE)) ); + + // setDirty(); +} + +void ServerScoreboard::setSaveData(ScoreboardSaveData* data) { + // saveData = data; +} + +void ServerScoreboard::setDirty() { + // if (saveData != NULL) + //{ + // saveData->setDirty(); + // } +} + +std::vector >* +ServerScoreboard::getStartTrackingPackets(Objective* objective) { + return NULL; + + // vector > *packets = new vector >(); + // packets.push_back( shared_ptr( new + // SetObjectivePacket(objective, SetObjectivePacket::METHOD_ADD))); + + // for (int slot = 0; slot < DISPLAY_SLOTS; slot++) + //{ + // if (getDisplayObjective(slot) == objective) packets.push_back( + // shared_ptr( new + // SetDisplayObjectivePacket(slot, objective))); + // } + + // for (Score score : getPlayerScores(objective)) + //{ + // packets.push_back( shared_ptr( new SetScorePacket(score, + // SetScorePacket::METHOD_CHANGE))); + // } + + // return packets; +} + +void ServerScoreboard::startTrackingObjective(Objective* objective) { + // vector > *packets = + // getStartTrackingPackets(objective); + + // for (ServerPlayer player : server.getPlayers().players) + //{ + // for (Packet packet : packets) + // { + // player.connection.send(packet); + // } + // } + + // trackedObjectives.push_back(objective); +} + +std::vector >* ServerScoreboard::getStopTrackingPackets( + Objective* objective) { + return NULL; + + // vector > *packets = new ArrayList(); + // packets->push_back( shared_ptr > *packets = getStopTrackingPackets(objective); + + // for (ServerPlayer player : server.getPlayers().players) + //{ + // for (Packet packet : packets) + // { + // player->connection->send(packet); + // } + // } + + // trackedObjectives.remove(objective); +} + +int ServerScoreboard::getObjectiveDisplaySlotCount(Objective* objective) { + return 0; + // int count = 0; + + // for (int slot = 0; slot < DISPLAY_SLOTS; slot++) + //{ + // if (getDisplayObjective(slot) == objective) count++; + // } + + // return count; +} \ No newline at end of file diff --git a/Minecraft.Client/Network/ServerScoreboard.h b/Minecraft.Client/Network/ServerScoreboard.h new file mode 100644 index 000000000..e06bd569b --- /dev/null +++ b/Minecraft.Client/Network/ServerScoreboard.h @@ -0,0 +1,45 @@ +#pragma once + +#include "../../Minecraft.World/Scores/Scoreboard.h" + +class MinecraftServer; +class ScoreboardSaveData; +class Score; +class Objective; +class PlayerTeam; + +class ServerScoreboard : public Scoreboard { +private: + MinecraftServer* server; + std::unordered_set trackedObjectives; + ScoreboardSaveData* saveData; + +public: + ServerScoreboard(MinecraftServer* server); + + MinecraftServer* getServer(); + void onScoreChanged(Score* score); + void onPlayerRemoved(const std::wstring& player); + void setDisplayObjective(int slot, Objective* objective); + void addPlayerToTeam(const std::wstring& player, PlayerTeam* team); + void removePlayerFromTeam(const std::wstring& player, PlayerTeam* team); + void onObjectiveAdded(Objective* objective); + void onObjectiveChanged(Objective* objective); + void onObjectiveRemoved(Objective* objective); + void onTeamAdded(PlayerTeam* team); + void onTeamChanged(PlayerTeam* team); + void onTeamRemoved(PlayerTeam* team); + void setSaveData(ScoreboardSaveData* data); + +protected: + void setDirty(); + +public: + std::vector >* getStartTrackingPackets( + Objective* objective); + void startTrackingObjective(Objective* objective); + std::vector >* getStopTrackingPackets( + Objective* objective); + void stopTrackingObjective(Objective* objective); + int getObjectiveDisplaySlotCount(Objective* objective); +}; \ No newline at end of file