diff --git a/BUMP b/BUMP index a60b8fce..d99a1985 100644 --- a/BUMP +++ b/BUMP @@ -1 +1 @@ -1.0.1b +1.0.4b diff --git a/CMakeLists.txt b/CMakeLists.txt index ef16e8ee..b2f1066d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -237,7 +237,7 @@ endif() # item.h takes priority as some tile.h blocks are not meant to be accessed # for example: the wheat 'block' (stage 1 wheat crop) is NOT supposed to override the normal wheat item set(_item_map_inputs - "${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.World/Item.h" + "${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.World/Item.h" "${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.World/Tile.h" ) @@ -268,6 +268,32 @@ if(PLATFORM_NAME STREQUAL "Windows64") add_dependencies(Minecraft.Server GenerateItemNameMap) endif() +#neo: added - SDK generation +set(SDK_INPUT_DIRS + "${CMAKE_SOURCE_DIR}/Minecraft.World" + "${CMAKE_SOURCE_DIR}/Minecraft.Client" + "${CMAKE_SOURCE_DIR}/include" + "${CMAKE_SOURCE_DIR}/Minecraft.Client/${PLATFORM_NAME}/4JLibs" +) + +set(SDK_OUTPUT "${CMAKE_BINARY_DIR}/Minecraft.Client/$/sdk.h") +add_custom_command( + OUTPUT "${SDK_OUTPUT}" + COMMAND ${CMAKE_COMMAND} + "-DINPUT_DIRS=${SDK_INPUT_DIRS}" + "-DOUTPUT_FILE=${SDK_OUTPUT}" + -P "${CMAKE_SOURCE_DIR}/cmake/GenerateSdk.cmake" + DEPENDS + "${CMAKE_SOURCE_DIR}/cmake/GenerateSdk.cmake" + COMMENT "Generating sdk.h..." + VERBATIM +) + +add_custom_target(GenerateSdk ALL + DEPENDS "${SDK_OUTPUT}" +) + +set_property(TARGET GenerateSdk PROPERTY FOLDER "Build") target_include_directories(Minecraft.Client PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/generated" ) diff --git a/Minecraft.Client/ClientConnection.cpp b/Minecraft.Client/ClientConnection.cpp index d23d5011..e2988019 100644 --- a/Minecraft.Client/ClientConnection.cpp +++ b/Minecraft.Client/ClientConnection.cpp @@ -61,6 +61,8 @@ #include "Windows64/Network/WinsockNetLayer.h" #endif +#include "../Minecraft.World/Recipes.h" + #ifdef _DURANGO #include "../Minecraft.World/DurangoStats.h" @@ -137,6 +139,9 @@ ClientConnection::ClientConnection(Minecraft *minecraft, Socket *socket, int iUs maxPlayers = 20; m_isForkServer = false; + m_recivedRecipeRegistyUpdate = false; + m_recivedCreativeRegistyUpdate = false; + this->minecraft = minecraft; if( iUserIndex < 0 ) @@ -246,6 +251,14 @@ void ClientConnection::handleLogin(shared_ptr packet) { if (done) return; + if (!m_recivedRecipeRegistyUpdate) { + Recipes::getInstance()->loadFromLocal(); + } + + if (!m_recivedCreativeRegistyUpdate) { + IUIScene_CreativeMenu::loadFromLocal(); + } + PlayerUID OnlineXuid; ProfileManager.GetXUID(m_userIndex,&OnlineXuid,true); // online xuid MOJANG_DATA *pMojangData = nullptr; @@ -4043,6 +4056,16 @@ void ClientConnection::handleCustomPayload(shared_ptr custo } } } + else if (CustomPayloadPacket::UPDATE_RECIPE_REGISTRY.compare(customPayloadPacket->identifier) == 0) { + this->m_recivedRecipeRegistyUpdate = true; + + Recipes::getInstance()->loadFromPacket(customPayloadPacket->data); + } + else if (CustomPayloadPacket::UPDATE_CREATIVE_REGISTRY.compare(customPayloadPacket->identifier) == 0) { + this->m_recivedCreativeRegistyUpdate = true; + + IUIScene_CreativeMenu::loadFromPacket(customPayloadPacket->data); + } } Connection *ClientConnection::getConnection() diff --git a/Minecraft.Client/ClientConnection.h b/Minecraft.Client/ClientConnection.h index ad78cac9..4e60b7d4 100644 --- a/Minecraft.Client/ClientConnection.h +++ b/Minecraft.Client/ClientConnection.h @@ -49,6 +49,8 @@ private: std::unordered_set m_trackedEntityIds; std::unordered_set m_visibleChunks; bool m_isForkServer; // true when connected to a fork server (received MC|ForkHello) + bool m_recivedRecipeRegistyUpdate; + bool m_recivedCreativeRegistyUpdate; static int64_t chunkKey(int x, int z) { return ((int64_t)x << 32) | ((int64_t)z & 0xFFFFFFFF); } diff --git a/Minecraft.Client/Common/App_Defines.h b/Minecraft.Client/Common/App_Defines.h index aad6bbcf..8d741041 100644 --- a/Minecraft.Client/Common/App_Defines.h +++ b/Minecraft.Client/Common/App_Defines.h @@ -56,7 +56,8 @@ enum EGameHostOptionWorldSize e_worldSize_Classic, e_worldSize_Small, e_worldSize_Medium, - e_worldSize_Large + e_worldSize_Large, + e_worldSize_Expanded }; diff --git a/Minecraft.Client/Common/Consoles_App.cpp b/Minecraft.Client/Common/Consoles_App.cpp index 92a57990..db8373ec 100644 --- a/Minecraft.Client/Common/Consoles_App.cpp +++ b/Minecraft.Client/Common/Consoles_App.cpp @@ -1,4 +1,4 @@ -#include "stdafx.h" +#include "stdafx.h" #include "../../Minecraft.World/net.minecraft.world.entity.item.h" #include "../../Minecraft.World/net.minecraft.world.entity.player.h" #include "../../Minecraft.World/net.minecraft.world.level.tile.entity.h" @@ -27,6 +27,9 @@ #include "../GameMode.h" #include "../Xbox/Social/SocialManager.h" #include "Tutorial/TutorialMode.h" +#ifdef _WINDOWS64 +#include "../Windows64/Network/WinsockNetLayer.h" // HUCKLE - added for quit on disconnect +#endif #if defined _XBOX || defined _WINDOWS64 #include "../Xbox/XML/ATGXmlParser.h" #include "../Xbox/XML/xmlFilesCallback.h" @@ -450,7 +453,7 @@ void CMinecraftApp::SetAction(int iPad, eXuiAction action, LPVOID param) bool CMinecraftApp::IsAppPaused() { -#if defined(_XBOX_ONE) || defined(__ORBIS__) +#if defined(_XBOX_ONE) || defined(__ORBIS__) || defined(_WINDOWS64) bool paused = m_bIsAppPaused; EnterCriticalSection(&m_saveNotificationCriticalSection); if( g_NetworkManager.IsLocalGame() && g_NetworkManager.GetPlayerCount() == 1 ) @@ -3445,6 +3448,15 @@ void CMinecraftApp::HandleXuiActions(void) SetAction(i,eAppAction_Idle); + // HUCKLE - added for quit game on disconnect +#ifdef _WINDOWS64 + if(g_Win64MultiplayerQuitOnDisconnect == true) + { + app.ExitGame(); + return; + } +#endif + // If we're already leaving don't exit if (g_NetworkManager.IsLeavingGame()) { @@ -9670,8 +9682,9 @@ bool CMinecraftApp::DLCContentRetrieved(eDLCMarketplaceType eType) void CMinecraftApp::SetAdditionalSkinBoxes(DWORD dwSkinID, SKIN_BOX *SkinBoxA, DWORD dwSkinBoxC) { - EntityRenderer *renderer = EntityRenderDispatcher::instance->getRenderer(eTYPE_PLAYER); - Model *pModel = renderer->getModel(); + EntityRenderDispatcher *dispatcher = EntityRenderDispatcher::instance; + EntityRenderer *renderer = dispatcher ? dispatcher->getRenderer(eTYPE_PLAYER) : nullptr; + Model *pModel = renderer ? renderer->getModel() : nullptr; vector *pvModelPart = new vector; vector *pvSkinBoxes = new vector; @@ -9702,8 +9715,9 @@ void CMinecraftApp::SetAdditionalSkinBoxes(DWORD dwSkinID, SKIN_BOX *SkinBoxA, D vector * CMinecraftApp::SetAdditionalSkinBoxes(DWORD dwSkinID, vector *pvSkinBoxA) { - EntityRenderer *renderer = EntityRenderDispatcher::instance->getRenderer(eTYPE_PLAYER); - Model *pModel = renderer->getModel(); + EntityRenderDispatcher *dispatcher = EntityRenderDispatcher::instance; + EntityRenderer *renderer = dispatcher ? dispatcher->getRenderer(eTYPE_PLAYER) : nullptr; + Model *pModel = renderer ? renderer->getModel() : nullptr; vector *pvModelPart = new vector; EnterCriticalSection( &csAdditionalModelParts ); diff --git a/Minecraft.Client/Common/UI/IUIScene_CreativeMenu.cpp b/Minecraft.Client/Common/UI/IUIScene_CreativeMenu.cpp index d452083a..ac1dbdfc 100644 --- a/Minecraft.Client/Common/UI/IUIScene_CreativeMenu.cpp +++ b/Minecraft.Client/Common/UI/IUIScene_CreativeMenu.cpp @@ -1,3 +1,4 @@ +#include "IUIScene_CreativeMenu.h" #include "stdafx.h" #include "IUIScene_CreativeMenu.h" @@ -22,6 +23,56 @@ vector< shared_ptr > IUIScene_CreativeMenu::categoryGroups[eCreati #define ITEM_AUX(id, aux) list->push_back( shared_ptr(new ItemInstance(id, 1, aux)) ); #define DEF(index) list = &categoryGroups[index]; +void IUIScene_CreativeMenu::_wipeCreativeItems() { + for (int i = 0; i < eCreativeInventoryGroupsCount; i++) { + IUIScene_CreativeMenu::categoryGroups[i].clear(); + } +} + +void IUIScene_CreativeMenu::loadFromLocal() { + IUIScene_CreativeMenu::_wipeCreativeItems(); + IUIScene_CreativeMenu::staticCtor(); +} + +void IUIScene_CreativeMenu::loadFromPacket(byteArray packetData) { + ByteArrayInputStream bais(packetData); + DataInputStream input(&bais); + + IUIScene_CreativeMenu::_wipeCreativeItems(); + + for (int i = 0; i < eCreativeInventoryGroupsCount; i++) { + int itemCount = input.readShort(); + + for (int j = 0; j < itemCount; j++) { + int itemId = input.readShort(); + int itemAux = input.readShort(); + + shared_ptr item = std::make_shared(itemId, 1, 0); + item->setRawAuxValue(itemAux); + item->tag = Packet::readNbt(&input); + + IUIScene_CreativeMenu::categoryGroups[i].emplace_back(item); + } + } + +} + +std::shared_ptr IUIScene_CreativeMenu::createUpdatePacket() { + ByteArrayOutputStream baos; + DataOutputStream dos(&baos); + + for (int i = 0; i < eCreativeInventoryGroupsCount; i++) { + dos.writeShort(IUIScene_CreativeMenu::categoryGroups[i].size()); + for (shared_ptr& item : IUIScene_CreativeMenu::categoryGroups[i]) { + dos.writeShort(item->id); + dos.writeShort(item->getAuxValue()); + Packet::writeNbt(item->tag, &dos); + } + } + + return std::make_shared(CustomPayloadPacket::UPDATE_CREATIVE_REGISTRY, baos.toByteArray()); +} + void IUIScene_CreativeMenu::staticCtor() { vector< shared_ptr > *list; @@ -802,55 +853,56 @@ void IUIScene_CreativeMenu::staticCtor() ITEM_AUX(Item::potion_Id,MACRO_MAKEPOTION_AUXVAL(MASK_SPLASH, MASK_LEVEL2EXTENDED, MASK_JUMPBOOST)) // end of tu31 potions + if (specs == nullptr) { + specs = new TabSpec * [eCreativeInventoryTab_COUNT]; - specs = new TabSpec*[eCreativeInventoryTab_COUNT]; - - // Top Row - ECreative_Inventory_Groups blocksGroup[] = {eCreativeInventory_BuildingBlocks}; - specs[eCreativeInventoryTab_BuildingBlocks] = new TabSpec(L"Structures", IDS_GROUPNAME_BUILDING_BLOCKS, 1, blocksGroup); + // Top Row + ECreative_Inventory_Groups blocksGroup[] = { eCreativeInventory_BuildingBlocks }; + specs[eCreativeInventoryTab_BuildingBlocks] = new TabSpec(L"Structures", IDS_GROUPNAME_BUILDING_BLOCKS, 1, blocksGroup); #ifndef _CONTENT_PACKAGE - ECreative_Inventory_Groups decorationsGroup[] = {eCreativeInventory_Decoration}; - ECreative_Inventory_Groups debugDecorationsGroup[] = {eCreativeInventory_ArtToolsDecorations}; - specs[eCreativeInventoryTab_Decorations] = new TabSpec(L"Decoration", IDS_GROUPNAME_DECORATIONS, 1, decorationsGroup, 0, nullptr, 1, debugDecorationsGroup); + ECreative_Inventory_Groups decorationsGroup[] = { eCreativeInventory_Decoration }; + ECreative_Inventory_Groups debugDecorationsGroup[] = { eCreativeInventory_ArtToolsDecorations }; + specs[eCreativeInventoryTab_Decorations] = new TabSpec(L"Decoration", IDS_GROUPNAME_DECORATIONS, 1, decorationsGroup, 0, nullptr, 1, debugDecorationsGroup); #else - ECreative_Inventory_Groups decorationsGroup[] = {eCreativeInventory_Decoration}; - specs[eCreativeInventoryTab_Decorations] = new TabSpec(L"Decoration", IDS_GROUPNAME_DECORATIONS, 1, decorationsGroup); + ECreative_Inventory_Groups decorationsGroup[] = { eCreativeInventory_Decoration }; + specs[eCreativeInventoryTab_Decorations] = new TabSpec(L"Decoration", IDS_GROUPNAME_DECORATIONS, 1, decorationsGroup); #endif - ECreative_Inventory_Groups redAndTranGroup[] = {eCreativeInventory_Transport, eCreativeInventory_Redstone}; - specs[eCreativeInventoryTab_RedstoneAndTransport] = new TabSpec(L"RedstoneAndTransport", IDS_GROUPNAME_REDSTONE_AND_TRANSPORT, 2, redAndTranGroup); + ECreative_Inventory_Groups redAndTranGroup[] = { eCreativeInventory_Transport, eCreativeInventory_Redstone }; + specs[eCreativeInventoryTab_RedstoneAndTransport] = new TabSpec(L"RedstoneAndTransport", IDS_GROUPNAME_REDSTONE_AND_TRANSPORT, 2, redAndTranGroup); - ECreative_Inventory_Groups materialsGroup[] = {eCreativeInventory_Materials}; - specs[eCreativeInventoryTab_Materials] = new TabSpec(L"Materials", IDS_GROUPNAME_MATERIALS, 1, materialsGroup); + ECreative_Inventory_Groups materialsGroup[] = { eCreativeInventory_Materials }; + specs[eCreativeInventoryTab_Materials] = new TabSpec(L"Materials", IDS_GROUPNAME_MATERIALS, 1, materialsGroup); - ECreative_Inventory_Groups foodGroup[] = {eCreativeInventory_Food}; - specs[eCreativeInventoryTab_Food] = new TabSpec(L"Food", IDS_GROUPNAME_FOOD, 1, foodGroup); + ECreative_Inventory_Groups foodGroup[] = { eCreativeInventory_Food }; + specs[eCreativeInventoryTab_Food] = new TabSpec(L"Food", IDS_GROUPNAME_FOOD, 1, foodGroup); - ECreative_Inventory_Groups toolsGroup[] = {eCreativeInventory_ToolsArmourWeapons}; - specs[eCreativeInventoryTab_ToolsWeaponsArmor] = new TabSpec(L"Tools", IDS_GROUPNAME_TOOLS_WEAPONS_ARMOR, 1, toolsGroup); + ECreative_Inventory_Groups toolsGroup[] = { eCreativeInventory_ToolsArmourWeapons }; + specs[eCreativeInventoryTab_ToolsWeaponsArmor] = new TabSpec(L"Tools", IDS_GROUPNAME_TOOLS_WEAPONS_ARMOR, 1, toolsGroup); - ECreative_Inventory_Groups brewingGroup[] = {eCreativeInventory_Brewing, eCreativeInventory_Potions_Level2_Extended, eCreativeInventory_Potions_Extended, eCreativeInventory_Potions_Level2, eCreativeInventory_Potions_Basic}; + ECreative_Inventory_Groups brewingGroup[] = { eCreativeInventory_Brewing, eCreativeInventory_Potions_Level2_Extended, eCreativeInventory_Potions_Extended, eCreativeInventory_Potions_Level2, eCreativeInventory_Potions_Basic }; - // Just use the text LT - the graphic doesn't fit in splitscreen either - // In 480p there's not enough room for the LT button, so use text instead - //if(!RenderManager.IsHiDef() && !RenderManager.IsWidescreen()) - { - specs[eCreativeInventoryTab_Brewing] = new TabSpec(L"Brewing", IDS_GROUPNAME_POTIONS_480, 5, brewingGroup); + // Just use the text LT - the graphic doesn't fit in splitscreen either + // In 480p there's not enough room for the LT button, so use text instead + //if(!RenderManager.IsHiDef() && !RenderManager.IsWidescreen()) + { + specs[eCreativeInventoryTab_Brewing] = new TabSpec(L"Brewing", IDS_GROUPNAME_POTIONS_480, 5, brewingGroup); + } + // else + // { + // specs[eCreativeInventoryTab_Brewing] = new TabSpec(L"icon_brewing.png", IDS_GROUPNAME_POTIONS, 1, brewingGroup, 4, potionsGroup); + // } + +#ifndef _CONTENT_PACKAGE + ECreative_Inventory_Groups miscGroup[] = { eCreativeInventory_Misc }; + ECreative_Inventory_Groups debugMiscGroup[] = { eCreativeInventory_ArtToolsMisc }; + specs[eCreativeInventoryTab_Misc] = new TabSpec(L"Misc", IDS_GROUPNAME_MISCELLANEOUS, 1, miscGroup, 0, nullptr, 1, debugMiscGroup); +#else + ECreative_Inventory_Groups miscGroup[] = { eCreativeInventory_Misc }; + specs[eCreativeInventoryTab_Misc] = new TabSpec(L"Misc", IDS_GROUPNAME_MISCELLANEOUS, 1, miscGroup); +#endif } - // else - // { - // specs[eCreativeInventoryTab_Brewing] = new TabSpec(L"icon_brewing.png", IDS_GROUPNAME_POTIONS, 1, brewingGroup, 4, potionsGroup); - // } - -#ifndef _CONTENT_PACKAGE - ECreative_Inventory_Groups miscGroup[] = {eCreativeInventory_Misc}; - ECreative_Inventory_Groups debugMiscGroup[] = {eCreativeInventory_ArtToolsMisc}; - specs[eCreativeInventoryTab_Misc] = new TabSpec(L"Misc", IDS_GROUPNAME_MISCELLANEOUS, 1, miscGroup, 0, nullptr, 1, debugMiscGroup); -#else - ECreative_Inventory_Groups miscGroup[] = {eCreativeInventory_Misc}; - specs[eCreativeInventoryTab_Misc] = new TabSpec(L"Misc", IDS_GROUPNAME_MISCELLANEOUS, 1, miscGroup); -#endif } IUIScene_CreativeMenu::IUIScene_CreativeMenu() diff --git a/Minecraft.Client/Common/UI/IUIScene_CreativeMenu.h b/Minecraft.Client/Common/UI/IUIScene_CreativeMenu.h index 02625233..855e5198 100644 --- a/Minecraft.Client/Common/UI/IUIScene_CreativeMenu.h +++ b/Minecraft.Client/Common/UI/IUIScene_CreativeMenu.h @@ -1,6 +1,7 @@ #pragma once #include "IUIScene_AbstractContainerMenu.h" #include "../../../Minecraft.World/AbstractContainerMenu.h" +#include "../../../Minecraft.World/CustomPayloadPacket.h" // 4J Stu - This class is for code that is common between XUI and Iggy class SimpleContainer; @@ -100,10 +101,16 @@ protected: bool m_bCarryingCreativeItem; int m_creativeSlotX, m_creativeSlotY, m_inventorySlotX, m_inventorySlotY; + static void _wipeCreativeItems(); + public: static void staticCtor(); IUIScene_CreativeMenu(); + static void loadFromLocal(); + static void loadFromPacket(byteArray packetData); + + static std::shared_ptr createUpdatePacket(); protected: ECreativeInventoryTabs m_curTab; int m_tabDynamicPos[eCreativeInventoryTab_COUNT]; diff --git a/Minecraft.Client/Common/UI/UIController.cpp b/Minecraft.Client/Common/UI/UIController.cpp index e88f76c0..39fcb38a 100644 --- a/Minecraft.Client/Common/UI/UIController.cpp +++ b/Minecraft.Client/Common/UI/UIController.cpp @@ -5,6 +5,7 @@ #include "UIScene.h" #include "UIControl_Slider.h" #include "UIControl_TexturePackList.h" +#include "UIControl_CheckBox.h" #include "UIControl_AchievementsList.h" #include "UIScene_AchievementsMenu.h" #include "../../../Minecraft.World/StringHelpers.h" @@ -976,6 +977,7 @@ void UIController::tickInput() { #ifdef _WINDOWS64 m_mouseClickConsumedByScene = false; + UIControl* currHitCtrl = NULL; if (!g_KBMInput.IsMouseGrabbed() && g_KBMInput.IsKBMActive()) { UIScene *pScene = nullptr; @@ -1150,6 +1152,7 @@ void UIController::tickInput() hitControlId = -1; hitArea = INT_MAX; hitCtrl = NULL; + hitCtrl = ctrl; break; // ButtonList takes priority } if (type == UIControl::eAchievementList) @@ -1162,6 +1165,7 @@ void UIController::tickInput() hitControlId = -1; hitArea = INT_MAX; hitCtrl = NULL; + hitCtrl = ctrl; break; } if (type == UIControl::eTexturePackList) @@ -1210,6 +1214,8 @@ void UIController::tickInput() } } } + currHitCtrl = hitCtrl; + UpdateCursorIcon(currHitCtrl); } } @@ -1336,6 +1342,27 @@ void UIController::tickInput() } } +void UIController::UpdateCursorIcon(UIControl *hitCtrl) +{ + // from WinUser.h + if (hitCtrl && (hitCtrl->getControlType() == UIControl::eButton || hitCtrl->getControlType() == UIControl::eButtonList)) + g_KBMInput.SetCursorIcon(MAKEINTRESOURCEW(IDC_HAND)); + else if (hitCtrl && (hitCtrl->getControlType() == UIControl::eSlider || hitCtrl->getControlType() == UIControl::eTexturePackList)) + g_KBMInput.SetCursorIcon(MAKEINTRESOURCEW(IDC_SIZEWE)); + else if (hitCtrl && hitCtrl->getControlType() == UIControl::eTextInput) + g_KBMInput.SetCursorIcon(MAKEINTRESOURCEW(IDC_IBEAM)); + else if (hitCtrl && hitCtrl->getControlType() == UIControl::eCheckBox) // Show the cross sign shaped cursor only when the checkbox is disabled/grayed out + { + UIControl_CheckBox *pCheck = static_cast(hitCtrl); + if (pCheck && !pCheck->IsEnabled()) + g_KBMInput.SetCursorIcon(MAKEINTRESOURCEW(IDC_NO)); + else + g_KBMInput.SetCursorIcon(MAKEINTRESOURCEW(IDC_HAND)); + } + else + g_KBMInput.SetCursorIcon(MAKEINTRESOURCEW(IDC_ARROW)); +} + void UIController::handleInput() { // For each user, loop over each key type and send messages based on the state @@ -2218,6 +2245,7 @@ bool UIController::NavigateToScene(int iPad, EUIScene scene, void *initData, EUI SetMenuDisplayed(menuDisplayedPad,true); bool success = m_groups[static_cast(group)]->NavigateToScene(iPad, scene, initData, layer); if(success && group == eUIGroup_Fullscreen) setFullscreenMenuDisplayed(true); + UpdateCursorIcon(nullptr); LeaveCriticalSection(&m_navigationLock); timer.PrintElapsedTime(L"Navigate to scene"); @@ -2257,6 +2285,7 @@ bool UIController::NavigateBack(int iPad, bool forceUsePad, EUIScene eScene, EUI navComplete = m_groups[static_cast(eUIGroup_Fullscreen)]->NavigateBack(iPad, eScene, eLayer); if(!m_groups[static_cast(eUIGroup_Fullscreen)]->GetMenuDisplayed()) SetMenuDisplayed(XUSER_INDEX_ANY,false); } + UpdateCursorIcon(nullptr); return navComplete; } diff --git a/Minecraft.Client/Common/UI/UIController.h b/Minecraft.Client/Common/UI/UIController.h index 8c995d1e..63ab9475 100644 --- a/Minecraft.Client/Common/UI/UIController.h +++ b/Minecraft.Client/Common/UI/UIController.h @@ -275,6 +275,7 @@ public: // INPUT private: void tickInput(); + void UpdateCursorIcon(UIControl *hitCtrl); void handleInput(); void handleKeyPress(unsigned int iPad, unsigned int key); diff --git a/Minecraft.Client/Common/UI/UIScene_Intro.cpp b/Minecraft.Client/Common/UI/UIScene_Intro.cpp index f4aefc40..975df407 100644 --- a/Minecraft.Client/Common/UI/UIScene_Intro.cpp +++ b/Minecraft.Client/Common/UI/UIScene_Intro.cpp @@ -2,6 +2,12 @@ #include "UI.h" #include "UIScene_Intro.h" +// HUCKLE - added below for joining game on launch +#ifdef _WINDOWS64 +#include "../../Windows64/Network/WinsockNetLayer.h" +#include "../../User.h" +#endif + UIScene_Intro::UIScene_Intro(int iPad, void *initData, UILayer *parentLayer) : UIScene(iPad, parentLayer) { @@ -104,6 +110,82 @@ void UIScene_Intro::handleInput(int iPad, int key, bool repeat, bool pressed, bo } #elif defined _XBOX_ONE ui.NavigateToScene(0,eUIScene_MainMenu); +#elif defined _WINDOWS64 + // HUCKLE - added this for auto joining servers on game launch + // THANKS so much to DrPerky and GeorgeV22 for helping with this bit, honestly got stuck for 4 hours :sob: + if(g_Win64MultiplayerJoin == true) + { + int primaryPad = ProfileManager.GetPrimaryPad(); + + if (!ProfileManager.IsSignedIn(primaryPad) || ProfileManager.IsGuest(primaryPad)) + { + UINT uiIDA[1] = { IDS_OK }; + ui.RequestErrorMessage(IDS_MUST_SIGN_IN_TITLE, IDS_MUST_SIGN_IN_TEXT, uiIDA, 1); + ui.NavigateToScene(0, eUIScene_MainMenu); + return; + } + + app.ClearSignInChangeUsersMask(); + app.ReleaseSaveThumbnail(); + ProfileManager.SetLockedProfile(primaryPad); + ProfileManager.QuerySigninStatus(); + + if (!app.DLCInstallProcessCompleted()) + app.StartInstallDLCProcess(primaryPad); + + Minecraft* pMinecraft = Minecraft::GetInstance(); + pMinecraft->user->name = convStringToWstring(ProfileManager.GetGamertag(primaryPad)); + app.ApplyGameSettingsChanged(primaryPad); + + auto sessionInfo = std::make_unique(); + + // label and name + const wchar_t* defaultName = L""; + size_t nameLen = wcslen(defaultName); + + // ip and port + strncpy_s(sessionInfo->data.hostIP, g_Win64MultiplayerIP, sizeof(sessionInfo->data.hostIP) - 1); + sessionInfo->data.hostPort = g_Win64MultiplayerPort; + + // display label + sessionInfo->displayLabel = new wchar_t[nameLen + 1]; + wcscpy_s(sessionInfo->displayLabel, nameLen + 1, defaultName); + sessionInfo->displayLabelLength = static_cast(nameLen); + sessionInfo->displayLabelViewableStartIndex = 0; + + // name + wcsncpy_s(sessionInfo->data.hostName, XUSER_NAME_SIZE, defaultName, _TRUNCATE); + + // session ids + sessionInfo->sessionId = static_cast(inet_addr(g_Win64MultiplayerIP)) | + static_cast(g_Win64MultiplayerPort) << 32; + + // random props + sessionInfo->data.isReadyToJoin = true; + sessionInfo->data.isJoinable = true; + + DWORD dwLocalUsersMask = 0; + dwLocalUsersMask |= CGameNetworkManager::GetLocalPlayerMask(ProfileManager.GetPrimaryPad()); + + CGameNetworkManager::eJoinGameResult result = g_NetworkManager.JoinGame( sessionInfo.get(), dwLocalUsersMask ); + + if (result == CGameNetworkManager::JOINGAME_PENDING) + { + ConnectionProgressParams *param = new ConnectionProgressParams(); + param->iPad = ProfileManager.GetPrimaryPad(); + param->stringId = IDS_PROGRESS_CONNECTING; + param->showTooltips = true; + param->setFailTimer = false; + param->timerTime = 0; + param->cancelFunc = nullptr; + param->cancelFuncParam = nullptr; + ui.NavigateToScene(ProfileManager.GetPrimaryPad(), eUIScene_ConnectingProgress, param); + } + } + else + { + ui.NavigateToScene(0,eUIScene_SaveMessage); + } #else ui.NavigateToScene(0,eUIScene_SaveMessage); #endif @@ -169,6 +251,82 @@ void UIScene_Intro::handleAnimationEnd() { m_bAnimationEnded = true; } +#elif defined _WINDOWS64 + // HUCKLE - added this for auto joining servers on game launch + // THANKS so much to DrPerky and GeorgeV22 for helping with this bit, honestly got stuck for 4 hours :sob: + if(g_Win64MultiplayerJoin == true) + { + int primaryPad = ProfileManager.GetPrimaryPad(); + + if (!ProfileManager.IsSignedIn(primaryPad) || ProfileManager.IsGuest(primaryPad)) + { + UINT uiIDA[1] = { IDS_OK }; + ui.RequestErrorMessage(IDS_MUST_SIGN_IN_TITLE, IDS_MUST_SIGN_IN_TEXT, uiIDA, 1); + ui.NavigateToScene(0, eUIScene_MainMenu); + return; + } + + app.ClearSignInChangeUsersMask(); + app.ReleaseSaveThumbnail(); + ProfileManager.SetLockedProfile(primaryPad); + ProfileManager.QuerySigninStatus(); + + if (!app.DLCInstallProcessCompleted()) + app.StartInstallDLCProcess(primaryPad); + + Minecraft* pMinecraft = Minecraft::GetInstance(); + pMinecraft->user->name = convStringToWstring(ProfileManager.GetGamertag(primaryPad)); + app.ApplyGameSettingsChanged(primaryPad); + + auto sessionInfo = std::make_unique(); + + // label and name + const wchar_t* defaultName = L""; + size_t nameLen = wcslen(defaultName); + + // ip and port + strncpy_s(sessionInfo->data.hostIP, g_Win64MultiplayerIP, sizeof(sessionInfo->data.hostIP) - 1); + sessionInfo->data.hostPort = g_Win64MultiplayerPort; + + // display label + sessionInfo->displayLabel = new wchar_t[nameLen + 1]; + wcscpy_s(sessionInfo->displayLabel, nameLen + 1, defaultName); + sessionInfo->displayLabelLength = static_cast(nameLen); + sessionInfo->displayLabelViewableStartIndex = 0; + + // name + wcsncpy_s(sessionInfo->data.hostName, XUSER_NAME_SIZE, defaultName, _TRUNCATE); + + // session ids + sessionInfo->sessionId = static_cast(inet_addr(g_Win64MultiplayerIP)) | + static_cast(g_Win64MultiplayerPort) << 32; + + // random props + sessionInfo->data.isReadyToJoin = true; + sessionInfo->data.isJoinable = true; + + DWORD dwLocalUsersMask = 0; + dwLocalUsersMask |= CGameNetworkManager::GetLocalPlayerMask(ProfileManager.GetPrimaryPad()); + + CGameNetworkManager::eJoinGameResult result = g_NetworkManager.JoinGame( sessionInfo.get(), dwLocalUsersMask ); + + if (result == CGameNetworkManager::JOINGAME_PENDING) + { + ConnectionProgressParams *param = new ConnectionProgressParams(); + param->iPad = ProfileManager.GetPrimaryPad(); + param->stringId = IDS_PROGRESS_CONNECTING; + param->showTooltips = true; + param->setFailTimer = false; + param->timerTime = 0; + param->cancelFunc = nullptr; + param->cancelFuncParam = nullptr; + ui.NavigateToScene(ProfileManager.GetPrimaryPad(), eUIScene_ConnectingProgress, param); + } + } + else + { + ui.NavigateToScene(0,eUIScene_SaveMessage); + } #else ui.NavigateToScene(0,eUIScene_SaveMessage); #endif diff --git a/Minecraft.Client/GameRenderer.cpp b/Minecraft.Client/GameRenderer.cpp index 5aa2b74d..8800ca0c 100644 --- a/Minecraft.Client/GameRenderer.cpp +++ b/Minecraft.Client/GameRenderer.cpp @@ -149,6 +149,7 @@ GameRenderer::GameRenderer(Minecraft *mc) #ifndef MINECRAFT_SERVER_BUILD // 4J-PB - set up the local players iteminhand renderers here - needs to be done with lighting enabled so that the render geometry gets compiled correctly +#ifndef MINECRAFT_SERVER_BUILD glEnable(GL_LIGHTING); mc->localitemInHandRenderers[0] = new ItemInHandRenderer(mc);//itemInHandRenderer; mc->localitemInHandRenderers[1] = new ItemInHandRenderer(mc); diff --git a/Minecraft.Client/ItemFrameRenderer.cpp b/Minecraft.Client/ItemFrameRenderer.cpp index f8f0e226..b297c546 100644 --- a/Minecraft.Client/ItemFrameRenderer.cpp +++ b/Minecraft.Client/ItemFrameRenderer.cpp @@ -126,81 +126,75 @@ void ItemFrameRenderer::drawFrame(shared_ptr itemFrame) void ItemFrameRenderer::drawItem(shared_ptr entity) { - Minecraft *pMinecraft=Minecraft::GetInstance(); - shared_ptr instance = entity->getItem(); - if (instance == nullptr) return; + shared_ptr instance = entity->getItem(); + if (instance == nullptr) return; - shared_ptr itemEntity = std::make_shared(entity->level, 0, 0, 0, instance); - itemEntity->getItem()->count = 1; - itemEntity->bobOffs = 0; + shared_ptr itemEntity = std::make_shared(entity->level, 0, 0, 0, instance); + itemEntity->getItem()->count = 1; + itemEntity->bobOffs = 0; - glPushMatrix(); + glPushMatrix(); - glTranslatef((-7.25f / 16.0f) * Direction::STEP_X[entity->dir], -0.18f, (-7.25f / 16.0f) * Direction::STEP_Z[entity->dir]); - glRotatef(180 + entity->yRot, 0, 1, 0); - glRotatef(-45.0f * entity->getRotation(), 0, 0, 1); + glRotatef(180.0f + entity->yRot, 0, 1, 0); + glTranslatef(0.0f, 0.0f, -0.4375f); - static const float offsets[8][2] = { - { 0.0f, 0.0f }, - { -0.08f, -0.08f }, - { -0.16f, -0.16f }, - { -0.08f, -0.24f }, - { 0.0f, -0.32f }, - { 0.08f, -0.24f }, - { 0.16f, -0.16f }, - { 0.08f, -0.08f } - }; - int rotIndex = entity->getRotation() & 0x7; - glTranslatef(offsets[rotIndex][0], offsets[rotIndex][1], 0.0f); + int rotation = entity->getRotation(); + bool isMap = (itemEntity->getItem()->getItem() == Item::map); + int effectiveRotation = isMap ? 2 * (rotation % 4) : rotation; - if (itemEntity->getItem()->getItem() == Item::map) - { - entityRenderDispatcher->textures->bindTexture(&MAP_BACKGROUND_LOCATION); - Tesselator *t = Tesselator::getInstance(); + glRotatef(-45.0f * effectiveRotation, 0, 0, 1); + glTranslatef(0.0f, -0.41f/2, 0.0f); - glRotatef(180, 0, 1, 0); - glRotatef(180, 0, 0, 1); - glScalef(1.0f / 128.0f, 1.0f / 128.0f, 1.0f / 128.0f); - glTranslatef(-64.0f, -87.0f, -3.0f); - glNormal3f(0, 0, -1); - t->begin(); - int vo = 7; - t->vertexUV(0.0f, 128.0f, 0.0f, 0.0f, 1.0f); - t->vertexUV(128.0f, 128.0f, 0.0f, 1.0f, 1.0f); - t->vertexUV(128.0f, 0.0f, 0.0f, 1.0f, 0.0f); - t->vertexUV(0.0f, 0.0f, 0.0f, 0.0f, 0.0f); - t->end(); + if (isMap) + { + //entityRenderDispatcher->textures->bindTexture(&MAP_BACKGROUND_LOCATION); + //Tesselator *t = Tesselator::getInstance(); - shared_ptr data = Item::map->getSavedData(itemEntity->getItem(), entity->level); - if (data != nullptr) - { - entityRenderDispatcher->itemInHandRenderer->minimap->render(nullptr, entityRenderDispatcher->textures, data, entity->entityId); - } - } - else - { - if (itemEntity->getItem()->getItem() == Item::compass) - { - CompassTexture *ct = CompassTexture::instance; - double compassRot = ct->rot; - double compassRotA = ct->rota; - ct->rot = 0; - ct->rota = 0; - ct->updateFromPosition(entity->level, entity->x, entity->z, Mth::wrapDegrees( static_cast(180 + entity->dir * 90) ), false, true); - ct->rot = compassRot; - ct->rota = compassRotA; - } + glRotatef(180, 0, 1, 0); + glRotatef(180, 0, 0, 1); + glScalef(1.0f / 128.0f, 1.0f / 128.0f, 1.0f / 128.0f); + glTranslatef(-64.0f, -87.0f, -3.0f); - EntityRenderDispatcher::instance->render(itemEntity, 0, 0, 0, 0, 0, true); + //glNormal3f(0, 0, -1); + //t->begin(); + //t->vertexUV(0.0f, 128.0f, 0.0f, 0.0f, 1.0f); + //t->vertexUV(128.0f, 128.0f, 0.0f, 1.0f, 1.0f); + //t->vertexUV(128.0f, 0.0f, 0.0f, 1.0f, 0.0f); + //t->vertexUV(0.0f, 0.0f, 0.0f, 0.0f, 0.0f); + //t->end(); - if (itemEntity->getItem()->getItem() == Item::compass) - { - CompassTexture *ct = CompassTexture::instance; - ct->cycleFrames(); - } - } - - glPopMatrix(); + shared_ptr data = Item::map->getSavedData(itemEntity->getItem(), entity->level); + if (data != nullptr) + { + entityRenderDispatcher->itemInHandRenderer->minimap->render( + nullptr, entityRenderDispatcher->textures, data, entity->entityId); + } + } + else + { + if (itemEntity->getItem()->getItem() == Item::compass) + { + CompassTexture *ct = CompassTexture::instance; + double compassRot = ct->rot; + double compassRotA = ct->rota; + ct->rot = 0; + ct->rota = 0; + ct->updateFromPosition(entity->level, entity->x, entity->z, + Mth::wrapDegrees(static_cast(180 + entity->dir * 90)), false, true); + ct->rot = compassRot; + ct->rota = compassRotA; + } + + EntityRenderDispatcher::instance->render(itemEntity, 0, 0, 0, 0, 0, true); + + if (itemEntity->getItem()->getItem() == Item::compass) + { + CompassTexture *ct = CompassTexture::instance; + ct->cycleFrames(); + } + } + + glPopMatrix(); } diff --git a/Minecraft.Client/Minecraft.cpp b/Minecraft.Client/Minecraft.cpp index 149f2728..8f67b1f4 100644 --- a/Minecraft.Client/Minecraft.cpp +++ b/Minecraft.Client/Minecraft.cpp @@ -1268,6 +1268,8 @@ void Minecraft::applyFrameMouseLook() void Minecraft::run_middle() { + pause = app.IsAppPaused(); + static int64_t lastTime = 0; static bool bFirstTimeIntoGame = true; static bool bAutosaveTimerSet=false; diff --git a/Minecraft.Client/MinecraftServer.cpp b/Minecraft.Client/MinecraftServer.cpp index 621af1c5..420bba64 100644 --- a/Minecraft.Client/MinecraftServer.cpp +++ b/Minecraft.Client/MinecraftServer.cpp @@ -1848,7 +1848,7 @@ void MinecraftServer::run(int64_t seed, void *lpParameter) lastTime = now; // 4J Added ability to pause the server - if( !m_isServerPaused ) + if( !m_isServerPaused && !app.IsAppPaused() ) { bool didTick = false; if (levels[0]->allPlayersAreSleeping()) diff --git a/Minecraft.Client/MinecraftServer.h b/Minecraft.Client/MinecraftServer.h index 6b76c194..7be94a71 100644 --- a/Minecraft.Client/MinecraftServer.h +++ b/Minecraft.Client/MinecraftServer.h @@ -266,7 +266,7 @@ private: #endif #endif - bool IsServerPaused() { return m_isServerPaused; } + private: // 4J Added @@ -291,6 +291,7 @@ public: const wstring& getSaveFolderName() const { return m_saveFolderName; } void Suspend(); bool IsSuspending(); + bool IsServerPaused() { return m_isServerPaused; } // 4J Stu - A load of functions were all added in 1.0.1 in the ServerInterface, but I don't think we need any of them }; diff --git a/Minecraft.Client/MultiPlayerChunkCache.cpp b/Minecraft.Client/MultiPlayerChunkCache.cpp index 19792432..f8f6c584 100644 --- a/Minecraft.Client/MultiPlayerChunkCache.cpp +++ b/Minecraft.Client/MultiPlayerChunkCache.cpp @@ -15,8 +15,8 @@ MultiPlayerChunkCache::MultiPlayerChunkCache(Level *level) XZSIZE = level->dimension->getXZSize(); // 4J Added XZOFFSET = XZSIZE/2; // 4J Added m_XZSize = XZSIZE; - hasData = new bool[XZSIZE * XZSIZE]; - memset(hasData, 0, sizeof(bool) * XZSIZE * XZSIZE); + hasData = new bool[LEVEL_MIN_WIDTH * LEVEL_MIN_WIDTH]; + memset(hasData, 0, sizeof(bool) * LEVEL_MIN_WIDTH * LEVEL_MIN_WIDTH); emptyChunk = new EmptyLevelChunk(level, byteArray(16 * 16 * Level::maxBuildHeight), 0, 0); @@ -93,8 +93,8 @@ MultiPlayerChunkCache::MultiPlayerChunkCache(Level *level) this->level = level; - this->cache = new LevelChunk *[XZSIZE * XZSIZE]; - memset(this->cache, 0, XZSIZE * XZSIZE * sizeof(LevelChunk *)); + this->cache = new LevelChunk *[LEVEL_MIN_WIDTH * LEVEL_MIN_WIDTH]; + memset(this->cache, 0, sizeof(LevelChunk*) * LEVEL_MIN_WIDTH * LEVEL_MIN_WIDTH); InitializeCriticalSectionAndSpinCount(&m_csLoadCreate,4000); } @@ -129,10 +129,11 @@ bool MultiPlayerChunkCache::reallyHasChunk(int x, int z) // Check we're in range of the stored level - if we aren't, then consider that we do have that chunk as we'll be able to use the water chunk there if( ( ix < 0 ) || ( ix >= XZSIZE ) ) return true; if( ( iz < 0 ) || ( iz >= XZSIZE ) ) return true; - int idx = ix * XZSIZE + iz; + + int idx = wrapCoord(x, LEVEL_MIN_WIDTH) * LEVEL_MIN_WIDTH + wrapCoord(z, LEVEL_MIN_WIDTH); LevelChunk *chunk = cache[idx]; - if( chunk == nullptr ) + if (chunk == nullptr || chunk->x != x || chunk->z != z) { return false; } @@ -145,10 +146,11 @@ void MultiPlayerChunkCache::drop(const int x, const int z) const int iz = z + XZOFFSET; if ((ix < 0) || (ix >= XZSIZE)) return; if ((iz < 0) || (iz >= XZSIZE)) return; - const int idx = ix * XZSIZE + iz; + + int idx = wrapCoord(x, LEVEL_MIN_WIDTH) * LEVEL_MIN_WIDTH + wrapCoord(z, LEVEL_MIN_WIDTH); LevelChunk* chunk = cache[idx]; - if (chunk != nullptr && !chunk->isEmpty()) + if (chunk != nullptr && !chunk->isEmpty() && chunk->x == x && chunk->z == z) { // Drop entities in the chunks, especially for the case when a player is dead // as they will not get the RemoveEntity packet if an entity is removed. @@ -168,11 +170,12 @@ LevelChunk *MultiPlayerChunkCache::create(int x, int z) // Check we're in range of the stored level if( ( ix < 0 ) || ( ix >= XZSIZE ) ) return ( waterChunk ? waterChunk : emptyChunk ); if( ( iz < 0 ) || ( iz >= XZSIZE ) ) return ( waterChunk ? waterChunk : emptyChunk ); - int idx = ix * XZSIZE + iz; + int idx = wrapCoord(x, LEVEL_MIN_WIDTH) * LEVEL_MIN_WIDTH + wrapCoord(z, LEVEL_MIN_WIDTH); + LevelChunk *chunk = cache[idx]; LevelChunk *lastChunk = chunk; - if( chunk == nullptr ) + if( chunk == nullptr || chunk->x != x || chunk->z != z ) { EnterCriticalSection(&m_csLoadCreate); @@ -251,10 +254,10 @@ LevelChunk *MultiPlayerChunkCache::getChunk(int x, int z) // Check we're in range of the stored level if( ( ix < 0 ) || ( ix >= XZSIZE ) ) return ( waterChunk ? waterChunk : emptyChunk ); if( ( iz < 0 ) || ( iz >= XZSIZE ) ) return ( waterChunk ? waterChunk : emptyChunk ); - int idx = ix * XZSIZE + iz; + int idx = wrapCoord(x, LEVEL_MIN_WIDTH) * LEVEL_MIN_WIDTH + wrapCoord(z, LEVEL_MIN_WIDTH); LevelChunk *chunk = cache[idx]; - if( chunk == nullptr ) + if( chunk == nullptr || chunk->x != x || chunk->z != z ) { return emptyChunk; } @@ -313,6 +316,6 @@ void MultiPlayerChunkCache::dataReceived(int x, int z) // Check we're in range of the stored level if( ( ix < 0 ) || ( ix >= XZSIZE ) ) return; if( ( iz < 0 ) || ( iz >= XZSIZE ) ) return; - int idx = ix * XZSIZE + iz; + int idx = wrapCoord(x, LEVEL_MIN_WIDTH) * LEVEL_MIN_WIDTH + wrapCoord(z, LEVEL_MIN_WIDTH); hasData[idx] = true; } \ No newline at end of file diff --git a/Minecraft.Client/MultiPlayerChunkCache.h b/Minecraft.Client/MultiPlayerChunkCache.h index 203f3124..e018790b 100644 --- a/Minecraft.Client/MultiPlayerChunkCache.h +++ b/Minecraft.Client/MultiPlayerChunkCache.h @@ -45,4 +45,9 @@ public: virtual void dataReceived(int x, int z); // 4J added virtual LevelChunk **getCache() { return cache; } // 4J added + + static inline int wrapCoord(int v, int size) { + int r = v % size; + return (r < 0) ? r + size : r; + } }; \ No newline at end of file diff --git a/Minecraft.Client/PlayerConnection.cpp b/Minecraft.Client/PlayerConnection.cpp index 90358ff7..f11ae5cd 100644 --- a/Minecraft.Client/PlayerConnection.cpp +++ b/Minecraft.Client/PlayerConnection.cpp @@ -183,7 +183,7 @@ void PlayerConnection::tick() // Ensure server-side player tick runs even when no move packet was received this tick. // Without this, environmental damage (drowning, fire, lava) is never applied to clients // that don't send frequent move packets. - if (!didTick && player != nullptr) + if (!didTick && player != nullptr && !server->IsServerPaused() && !app.IsAppPaused()) { player->doTick(false); } @@ -2417,7 +2417,7 @@ void PlayerConnection::handleCustomPayload(shared_ptr custo player->inventory->setItem(player->inventory->selected, sentItem); } } - else if (CustomPayloadPacket::QUICK_EQUIP_PACKET.compare(customPayloadPacket->identifier) == 0) { + /*else if (CustomPayloadPacket::QUICK_EQUIP_PACKET.compare(customPayloadPacket->identifier) == 0) { //ByteArrayInputStream bais(customPayloadPacket->data); //DataInputStream input(&bais); //shared_ptr sentItem = Packet::readItem(&input); @@ -2448,7 +2448,7 @@ void PlayerConnection::handleCustomPayload(shared_ptr custo //PlayerList* playerList = MinecraftServer::getInstance()->getPlayers(); //playerList->broadcastAll(std::make_shared(player->entityId, slot, sentItem)); - } + }*/ else if (CustomPayloadPacket::TRADER_SELECTION_PACKET.compare(customPayloadPacket->identifier) == 0) { ByteArrayInputStream bais(customPayloadPacket->data); diff --git a/Minecraft.Client/PlayerList.cpp b/Minecraft.Client/PlayerList.cpp index ebab50d2..72ebc66b 100644 --- a/Minecraft.Client/PlayerList.cpp +++ b/Minecraft.Client/PlayerList.cpp @@ -37,6 +37,8 @@ #include "Common/Network/Sony/NetworkPlayerSony.h" #endif +#include "../Minecraft.World/Recipes.h" + #if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD) #include "../Minecraft.Server/Access/Access.h" #include "../Minecraft.Server/Common/StringUtils.h" @@ -51,6 +53,7 @@ extern bool g_Win64DedicatedServer; static unsigned int s_playerListTickCount = 0; static const int kIdentityResponseGraceTicks = 200; // 10 seconds at 20 TPS #endif +#include "../Minecraft.Client/Common/UI/IUIScene_CreativeMenu.h" // 4J - this class is fairly substantially altered as there didn't seem any point in porting code for banning, whitelisting, ops etc. @@ -300,6 +303,9 @@ bool PlayerList::placeNewPlayer(Connection *connection, shared_ptr app.DebugPrintf("RECONNECT: placeNewPlayer smallId=%d entityId=%d dim=%d\n", newSmallId, player->entityId, level->dimension->id); + playerConnection->send(Recipes::getInstance()->createUpdatePacket()); + playerConnection->send(IUIScene_CreativeMenu::createUpdatePacket()); + playerConnection->send(std::make_shared(L"", player->entityId, level->getLevelData()->getGenerator(), level->getSeed(), player->gameMode->getGameModeForPlayer()->getId(), diff --git a/Minecraft.Client/Windows64/KeyboardMouseInput.cpp b/Minecraft.Client/Windows64/KeyboardMouseInput.cpp index be6efe90..3b6f5aec 100644 --- a/Minecraft.Client/Windows64/KeyboardMouseInput.cpp +++ b/Minecraft.Client/Windows64/KeyboardMouseInput.cpp @@ -390,6 +390,20 @@ float KeyboardMouseInput::GetLookY(float sensitivity) const return static_cast(-m_mouseDeltaY) * sensitivity; } +void KeyboardMouseInput::SetCursorIcon(LPCWSTR cursorName) +{ + HCURSOR hCursor = LoadCursorW(nullptr, cursorName); + if (hCursor) + { + SetCursor(hCursor); + + if (g_hWnd) + { + SetClassLongPtrW(g_hWnd, GCLP_HCURSOR, (LONG_PTR)hCursor); + } + } +} + void KeyboardMouseInput::OnChar(wchar_t c) { int next = (m_charBufferHead + 1) % CHAR_BUFFER_SIZE; diff --git a/Minecraft.Client/Windows64/KeyboardMouseInput.h b/Minecraft.Client/Windows64/KeyboardMouseInput.h index 23158ed2..ed98a03c 100644 --- a/Minecraft.Client/Windows64/KeyboardMouseInput.h +++ b/Minecraft.Client/Windows64/KeyboardMouseInput.h @@ -104,6 +104,8 @@ public: float GetLookX(float sensitivity) const; float GetLookY(float sensitivity) const; + void SetCursorIcon(LPCWSTR cursorName); + private: bool m_keyDown[MAX_KEYS]; bool m_keyDownPrev[MAX_KEYS]; diff --git a/Minecraft.Client/Windows64/Network/WinsockNetLayer.cpp b/Minecraft.Client/Windows64/Network/WinsockNetLayer.cpp index e763e3d5..904103b7 100644 --- a/Minecraft.Client/Windows64/Network/WinsockNetLayer.cpp +++ b/Minecraft.Client/Windows64/Network/WinsockNetLayer.cpp @@ -115,6 +115,7 @@ bool WinsockNetLayer::s_clientKeyStored = false; bool g_Win64MultiplayerHost = false; bool g_Win64MultiplayerJoin = false; +bool g_Win64MultiplayerQuitOnDisconnect = false; int g_Win64MultiplayerPort = WIN64_NET_DEFAULT_PORT; char g_Win64MultiplayerIP[256] = "127.0.0.1"; bool g_Win64DedicatedServer = false; diff --git a/Minecraft.Client/Windows64/Network/WinsockNetLayer.h b/Minecraft.Client/Windows64/Network/WinsockNetLayer.h index a4eb2fae..dc830c84 100644 --- a/Minecraft.Client/Windows64/Network/WinsockNetLayer.h +++ b/Minecraft.Client/Windows64/Network/WinsockNetLayer.h @@ -227,6 +227,7 @@ public: extern bool g_Win64MultiplayerHost; extern bool g_Win64MultiplayerJoin; +extern bool g_Win64MultiplayerQuitOnDisconnect; extern int g_Win64MultiplayerPort; extern char g_Win64MultiplayerIP[256]; extern bool g_Win64DedicatedServer; diff --git a/Minecraft.Client/Windows64/Windows64_Minecraft.cpp b/Minecraft.Client/Windows64/Windows64_Minecraft.cpp index c756043b..f0d58217 100644 --- a/Minecraft.Client/Windows64/Windows64_Minecraft.cpp +++ b/Minecraft.Client/Windows64/Windows64_Minecraft.cpp @@ -219,6 +219,7 @@ static Win64LaunchOptions ParseLaunchOptions() Win64LaunchOptions options = {}; options.screenMode = 0; + g_Win64MultiplayerQuitOnDisconnect = false; g_Win64MultiplayerJoin = false; g_Win64MultiplayerPort = WIN64_NET_DEFAULT_PORT; @@ -239,6 +240,10 @@ static Win64LaunchOptions ParseLaunchOptions() { CopyWideArgToAnsi(argv[++i], g_Win64Username, sizeof(g_Win64Username)); } + else if (_wcsicmp(argv[i], L"-quitondisconnect") == 0) + { + g_Win64MultiplayerQuitOnDisconnect = true; + } else if (_wcsicmp(argv[i], L"-ip") == 0 && (i + 1) < argc) { char ipBuf[256]; @@ -1759,9 +1764,7 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance, return 1; } g_bResizeReady = true; - - ui.ReloadSkin(); - + //app.TemporaryCreateGameStart(); //Sleep(10000); diff --git a/Minecraft.Client/Windows64Media/Media/languages.loc b/Minecraft.Client/Windows64Media/Media/languages.loc index 3fb5b1a0..4b11ca99 100644 Binary files a/Minecraft.Client/Windows64Media/Media/languages.loc and b/Minecraft.Client/Windows64Media/Media/languages.loc differ diff --git a/Minecraft.Client/Windows64Media/loc/stringsGeneric.xml b/Minecraft.Client/Windows64Media/loc/stringsGeneric.xml index 3938c39f..1b8d725a 100644 --- a/Minecraft.Client/Windows64Media/loc/stringsGeneric.xml +++ b/Minecraft.Client/Windows64Media/loc/stringsGeneric.xml @@ -9152,7 +9152,7 @@ All Ender Chests in a world are linked. Items placed into an Ender Chest are acc - {*ICON_SHANK_01*} + Rabbit Stew @@ -9622,4 +9622,8 @@ All Ender Chests in a world are linked. Items placed into an Ender Chest are acc Hold {*CONTROLLER_VK_Y*} to view Classic Crafting + + + Restores 5{*ICON_SHANK_01*}. + diff --git a/Minecraft.Server/ServerProperties.cpp b/Minecraft.Server/ServerProperties.cpp index 1b6c5c0a..525be788 100644 --- a/Minecraft.Server/ServerProperties.cpp +++ b/Minecraft.Server/ServerProperties.cpp @@ -628,6 +628,8 @@ static std::string WorldSizeToPropertyValue(int worldSize) return "medium"; case e_worldSize_Large: return "large"; + case e_worldSize_Expanded: + return "expanded"; case e_worldSize_Classic: default: return "classic"; @@ -644,6 +646,8 @@ static int WorldSizeToXzChunks(int worldSize) return LEVEL_WIDTH_MEDIUM; case e_worldSize_Large: return LEVEL_WIDTH_LARGE; + case e_worldSize_Expanded: + return LEVEL_WIDTH_EXPANDED; case e_worldSize_Classic: default: return LEVEL_WIDTH_CLASSIC; @@ -659,6 +663,7 @@ static int WorldSizeToHellScale(int worldSize) case e_worldSize_Medium: return HELL_LEVEL_SCALE_MEDIUM; case e_worldSize_Large: + case e_worldSize_Expanded: return HELL_LEVEL_SCALE_LARGE; case e_worldSize_Classic: default: @@ -694,6 +699,12 @@ static bool TryParseWorldSize(const std::string &lowered, int *outWorldSize) return true; } + if (lowered == "expanded" || lowered == "344" || lowered == "8") + { + *outWorldSize = e_worldSize_Expanded; + return true; + } + return false; } diff --git a/Minecraft.World/AddEntityPacket.cpp b/Minecraft.World/AddEntityPacket.cpp index f70082b8..99c5d0ab 100644 --- a/Minecraft.World/AddEntityPacket.cpp +++ b/Minecraft.World/AddEntityPacket.cpp @@ -54,7 +54,7 @@ AddEntityPacket::AddEntityPacket(shared_ptr e, int type, int data, int y void AddEntityPacket::read(DataInputStream *dis) // throws IOException TODO 4J JEV add throws statement { - id = dis->readShort(); + id = dis->readInt(); type = dis->readByte(); #ifdef _LARGE_WORLDS x = dis->readInt(); @@ -78,7 +78,7 @@ void AddEntityPacket::read(DataInputStream *dis) // throws IOException TODO 4J void AddEntityPacket::write(DataOutputStream *dos) // throws IOException TODO 4J JEV add throws statement { - dos->writeShort(id); + dos->writeInt(id); dos->writeByte(type); #ifdef _LARGE_WORLDS dos->writeInt(x); @@ -107,5 +107,5 @@ void AddEntityPacket::handle(PacketListener *listener) int AddEntityPacket::getEstimatedSize() { - return 11 + data > -1 ? 6 : 0; + return (11 + data > -1 ? 6 : 0) + 2; } diff --git a/Minecraft.World/AddMobPacket.cpp b/Minecraft.World/AddMobPacket.cpp index d5ccfe08..8aca1055 100644 --- a/Minecraft.World/AddMobPacket.cpp +++ b/Minecraft.World/AddMobPacket.cpp @@ -66,7 +66,7 @@ AddMobPacket::AddMobPacket(shared_ptr mob, int yRotp, int xRotp, i void AddMobPacket::read(DataInputStream *dis) //throws IOException { - id = dis->readShort(); + id = dis->readInt(); type = dis->readByte() & 0xff; #ifdef _LARGE_WORLDS x = dis->readInt(); @@ -90,7 +90,7 @@ void AddMobPacket::read(DataInputStream *dis) //throws IOException void AddMobPacket::write(DataOutputStream *dos) //throws IOException { - dos->writeShort(id); + dos->writeInt(id); dos->writeByte(type & 0xff); #ifdef _LARGE_WORLDS dos->writeInt(x); @@ -127,7 +127,7 @@ int AddMobPacket::getEstimatedSize() // 4J Stu - This is an incoming value which we aren't currently analysing //size += unpack->get } - return size; + return size + 2; } vector > *AddMobPacket::getUnpackedData() diff --git a/Minecraft.World/ChunkSource.h b/Minecraft.World/ChunkSource.h index 37b0ecc3..5b510ddb 100644 --- a/Minecraft.World/ChunkSource.h +++ b/Minecraft.World/ChunkSource.h @@ -7,12 +7,14 @@ class TilePos; // The maximum number of chunks that we can store #ifdef _LARGE_WORLDS // 4J Stu - Our default map (at zoom level 3) is 1024x1024 blocks (or 64 chunks) -#define LEVEL_MAX_WIDTH (5*64) //(6*54) - #define LEVEL_WIDTH_CLASSIC 54 #define LEVEL_WIDTH_SMALL 64 #define LEVEL_WIDTH_MEDIUM (3*64) #define LEVEL_WIDTH_LARGE (5*64) +#define LEVEL_WIDTH_EXPANDED (5*64) + 24 + + +#define LEVEL_MAX_WIDTH LEVEL_WIDTH_EXPANDED #else #define LEVEL_MAX_WIDTH 54 diff --git a/Minecraft.World/ComparatorTile.cpp b/Minecraft.World/ComparatorTile.cpp index af3b2fd7..cf534de3 100644 --- a/Minecraft.World/ComparatorTile.cpp +++ b/Minecraft.World/ComparatorTile.cpp @@ -1,4 +1,4 @@ -#include "stdafx.h" +#include "stdafx.h" #include "net.minecraft.world.item.h" #include "net.minecraft.world.level.h" #include "net.minecraft.world.level.redstone.h" @@ -134,33 +134,42 @@ bool ComparatorTile::shouldTurnOn(Level *level, int x, int y, int z, int data) int ComparatorTile::getInputSignal(Level *level, int x, int y, int z, int data) { - int result = DiodeTile::getInputSignal(level, x, y, z, data); + int result = DiodeTile::getInputSignal(level, x, y, z, data); - int dir = getDirection(data); - int xx = x + Direction::STEP_X[dir]; - int zz = z + Direction::STEP_Z[dir]; - int tile = level->getTile(xx, y, zz); + int dir = getDirection(data); + int xx = x + Direction::STEP_X[dir]; + int zz = z + Direction::STEP_Z[dir]; + int tile = level->getTile(xx, y, zz); - if (tile > 0) - { - if (Tile::tiles[tile]->hasAnalogOutputSignal()) - { - result = Tile::tiles[tile]->getAnalogOutputSignal(level, xx, y, zz, Direction::DIRECTION_OPPOSITE[dir]); - } - else if (result < Redstone::SIGNAL_MAX && Tile::isSolidBlockingTile(tile)) - { - xx += Direction::STEP_X[dir]; - zz += Direction::STEP_Z[dir]; - tile = level->getTile(xx, y, zz); + if (tile > 0) + { + if (Tile::tiles[tile]->hasAnalogOutputSignal()) + { + result = Tile::tiles[tile]->getAnalogOutputSignal(level, xx, y, zz, Direction::DIRECTION_OPPOSITE[dir]); + } + else if (result < Redstone::SIGNAL_MAX && Tile::isSolidBlockingTile(tile)) + { + xx += Direction::STEP_X[dir]; + zz += Direction::STEP_Z[dir]; + tile = level->getTile(xx, y, zz); - if (tile > 0 && Tile::tiles[tile]->hasAnalogOutputSignal()) - { - result = Tile::tiles[tile]->getAnalogOutputSignal(level, xx, y, zz, Direction::DIRECTION_OPPOSITE[dir]); - } - } - } + if (tile > 0 && Tile::tiles[tile]->hasAnalogOutputSignal()) + { + result = Tile::tiles[tile]->getAnalogOutputSignal(level, xx, y, zz, Direction::DIRECTION_OPPOSITE[dir]); + } + + else if (tile == 0) + { + shared_ptr frame = getItemFrame(level, xx, y, zz); + if (frame != nullptr) + { + result = frame->getAnalogOutput(); + } + } + } + } - return result; + return result; } shared_ptr ComparatorTile::getComparator(LevelSource *level, int x, int y, int z) @@ -277,4 +286,28 @@ shared_ptr ComparatorTile::newTileEntity(Level *level) bool ComparatorTile::TestUse() { return true; +} + +shared_ptr ComparatorTile::getItemFrame( + Level* level, + int x, + int y, + int z) +{ + AABB* box = AABB::newTemp( + x, + y, + z, + x + 1, + y + 1, + z + 1 + ); + + vector>* entities = + level->getEntitiesOfClass(typeid(ItemFrame), box); + + if (entities == nullptr || entities->size() != 1) + return nullptr; + + return dynamic_pointer_cast((*entities)[0]); } \ No newline at end of file diff --git a/Minecraft.World/ComparatorTile.h b/Minecraft.World/ComparatorTile.h index 8341cff7..ef1f92b2 100644 --- a/Minecraft.World/ComparatorTile.h +++ b/Minecraft.World/ComparatorTile.h @@ -2,6 +2,7 @@ #include "DiodeTile.h" #include "EntityTile.h" +#include "AABB.h" class ComparatorTileEntity; @@ -62,4 +63,5 @@ public: virtual bool triggerEvent(Level *level, int x, int y, int z, int b0, int b1); virtual shared_ptr newTileEntity(Level *level); virtual bool TestUse(); + shared_ptr ComparatorTile::getItemFrame(Level* level,int x,int y,int z); }; \ No newline at end of file diff --git a/Minecraft.World/Creeper.cpp b/Minecraft.World/Creeper.cpp index 3b4c8442..3f674891 100644 --- a/Minecraft.World/Creeper.cpp +++ b/Minecraft.World/Creeper.cpp @@ -26,6 +26,7 @@ void Creeper::_init() oldSwell = 0; maxSwell = 30; explosionRadius = 3; + ignited = false; } Creeper::Creeper(Level *level) : Monster( level ) @@ -190,3 +191,38 @@ void Creeper::thunderHit(const LightningBolt *lightningBolt) Monster::thunderHit(lightningBolt); entityData->set(DATA_IS_POWERED, static_cast(1)); } + +void Creeper::Ignite() +{ + setSwellDir(1); + ignited = true; +} + +bool Creeper::isIgnited() +{ + return ignited; +} + +bool Creeper::mobInteract(shared_ptr player) +{ + shared_ptr item = player->inventory->getSelected(); + + if (item == nullptr || item->id != Item::flintAndSteel_Id) + return Mob::mobInteract(player); + + playSound(eSoundType_FIRE_NEWIGNITE, 1, random->nextFloat() * 0.4f + 0.8f); + player->swing(); + + if (!level->isClientSide) + { + if (!isIgnited()) + { + Ignite(); + item->hurtAndBreak(1, player); + return true; + } + return Mob::mobInteract(player); + } + + return true; +} diff --git a/Minecraft.World/Creeper.h b/Minecraft.World/Creeper.h index d5cd4cff..ed9ff59d 100644 --- a/Minecraft.World/Creeper.h +++ b/Minecraft.World/Creeper.h @@ -21,6 +21,8 @@ private: int maxSwell; int explosionRadius; + bool ignited; + void _init(); public: @@ -34,6 +36,8 @@ public: virtual int getMaxFallDistance(); + virtual bool mobInteract(shared_ptr player); + protected: virtual void causeFallDamage(float distance); virtual void defineSynchedData(); @@ -61,5 +65,9 @@ protected: public: int getSwellDir(); void setSwellDir(int dir); - void thunderHit(const LightningBolt *lightningBolt) ; + void thunderHit(const LightningBolt *lightningBolt); + +public: + void Ignite(); + bool isIgnited(); }; diff --git a/Minecraft.World/CustomPayloadPacket.cpp b/Minecraft.World/CustomPayloadPacket.cpp index e6dacfeb..6b706d6f 100644 --- a/Minecraft.World/CustomPayloadPacket.cpp +++ b/Minecraft.World/CustomPayloadPacket.cpp @@ -5,30 +5,39 @@ #include "CustomPayloadPacket.h" // Mojang-defined custom packets -const wstring CustomPayloadPacket::CUSTOM_BOOK_PACKET = L"MC|BEdit"; -const wstring CustomPayloadPacket::CUSTOM_BOOK_SIGN_PACKET = L"MC|BSign"; -const wstring CustomPayloadPacket::TEXTURE_PACK_PACKET = L"MC|TPack"; -const wstring CustomPayloadPacket::TRADER_LIST_PACKET = L"MC|TrList"; -const wstring CustomPayloadPacket::TRADER_SELECTION_PACKET = L"MC|TrSel"; -const wstring CustomPayloadPacket::SET_ADVENTURE_COMMAND_PACKET = L"MC|AdvCdm"; -const wstring CustomPayloadPacket::SET_BEACON_PACKET = L"MC|Beacon"; -const wstring CustomPayloadPacket::SET_ITEM_NAME_PACKET = L"MC|ItemName"; +const wstring CustomPayloadPacket::CUSTOM_BOOK_PACKET = CreateVanillaPayloadKey(L"BEdit"); +const wstring CustomPayloadPacket::CUSTOM_BOOK_SIGN_PACKET = CreateVanillaPayloadKey(L"BSign"); +const wstring CustomPayloadPacket::TEXTURE_PACK_PACKET = CreateVanillaPayloadKey(L"TPack"); +const wstring CustomPayloadPacket::TRADER_LIST_PACKET = CreateVanillaPayloadKey(L"TrList"); +const wstring CustomPayloadPacket::TRADER_SELECTION_PACKET = CreateVanillaPayloadKey(L"TrSel"); -const wstring CustomPayloadPacket::CIPHER_KEY_CHANNEL = L"MC|CKey"; -const wstring CustomPayloadPacket::CIPHER_ACK_CHANNEL = L"MC|CAck"; -const wstring CustomPayloadPacket::CIPHER_ON_CHANNEL = L"MC|COn"; +// neoLegacy-defined custom packets +const wstring CustomPayloadPacket::UPDATE_RECIPE_REGISTRY = CreatePayloadKey(L"neo", L"UpdRReg"); +const wstring CustomPayloadPacket::UPDATE_CREATIVE_REGISTRY = CreatePayloadKey(L"neo", L"UpdCReg"); -const wstring CustomPayloadPacket::IDENTITY_TOKEN_ISSUE = L"MC|CTIssue"; -const wstring CustomPayloadPacket::IDENTITY_TOKEN_CHALLENGE = L"MC|CTChallenge"; -const wstring CustomPayloadPacket::IDENTITY_TOKEN_RESPONSE = L"MC|CTResponse"; +//todo: figure out if we should replace the packets in the comment section with a custom payload identifier +//comment section start +const wstring CustomPayloadPacket::SET_ADVENTURE_COMMAND_PACKET = CreateVanillaPayloadKey(L"AdvCdm"); +const wstring CustomPayloadPacket::SET_BEACON_PACKET = CreateVanillaPayloadKey(L"Beacon"); +const wstring CustomPayloadPacket::SET_ITEM_NAME_PACKET = CreateVanillaPayloadKey(L"ItemName"); -const wstring CustomPayloadPacket::FORK_HELLO_CHANNEL = L"MC|ForkHello"; -const wstring CustomPayloadPacket::FORK_PLAYER_LEAVE_CHANNEL = L"MC|ForkPLeave"; +const wstring CustomPayloadPacket::CIPHER_KEY_CHANNEL = CreateVanillaPayloadKey(L"CKey"); +const wstring CustomPayloadPacket::CIPHER_ACK_CHANNEL = CreateVanillaPayloadKey(L"CAck"); +const wstring CustomPayloadPacket::CIPHER_ON_CHANNEL = CreateVanillaPayloadKey(L"COn"); -const wstring CustomPayloadPacket::QUICK_EQUIP_PACKET = L"MC|QEquip"; -const wstring CustomPayloadPacket::QUICK_EQUIP_SERVER_PACKET = L"MC|QEquipServer"; +const wstring CustomPayloadPacket::IDENTITY_TOKEN_ISSUE = CreateVanillaPayloadKey(L"CTIssue"); +const wstring CustomPayloadPacket::IDENTITY_TOKEN_CHALLENGE = CreateVanillaPayloadKey(L"CTChallenge"); +const wstring CustomPayloadPacket::IDENTITY_TOKEN_RESPONSE = CreateVanillaPayloadKey(L"CTResponse"); -const wstring CustomPayloadPacket::ENCHANTMENT_LIST_PACKET = L"MC|EnchList"; +const wstring CustomPayloadPacket::FORK_HELLO_CHANNEL = CreateVanillaPayloadKey(L"ForkHello"); +const wstring CustomPayloadPacket::FORK_PLAYER_LEAVE_CHANNEL = CreateVanillaPayloadKey(L"ForkPLeave"); + +const wstring CustomPayloadPacket::ENCHANTMENT_LIST_PACKET = CreateVanillaPayloadKey(L"EnchList"); +//comment section end + +//removed cause its now handled on the server side +// const wstring CustomPayloadPacket::QUICK_EQUIP_PACKET = CreateVanillaPayloadKey(L"QEquip"); +// const wstring CustomPayloadPacket::QUICK_EQUIP_SERVER_PACKET = CreateVanillaPayloadKey(L"QEquipServer"); CustomPayloadPacket::CustomPayloadPacket() : length(0) diff --git a/Minecraft.World/CustomPayloadPacket.h b/Minecraft.World/CustomPayloadPacket.h index ae7163bf..4fd30f05 100644 --- a/Minecraft.World/CustomPayloadPacket.h +++ b/Minecraft.World/CustomPayloadPacket.h @@ -3,6 +3,9 @@ using namespace std; #include "Packet.h" +#define CreatePayloadKey(identifier, action) identifier L"|" action +#define CreateVanillaPayloadKey(action) CreatePayloadKey(L"MC", action) + class CustomPayloadPacket : public Packet, public enable_shared_from_this { public: @@ -17,6 +20,10 @@ public: static const wstring SET_BEACON_PACKET; static const wstring SET_ITEM_NAME_PACKET; + // neoLegacy-defined custom packets + static const wstring UPDATE_RECIPE_REGISTRY; + static const wstring UPDATE_CREATIVE_REGISTRY; + // Security: stream cipher handshake channels static const wstring CIPHER_KEY_CHANNEL; // server->client: carries 32-byte key (16 AES key + 16 IV) static const wstring CIPHER_ACK_CHANNEL; // client->server: ack (empty payload) @@ -32,8 +39,8 @@ public: static const wstring FORK_PLAYER_LEAVE_CHANNEL; // server->client: player disconnected (payload: UTF gamertag) // Fixes for MP related crashes - static const wstring QUICK_EQUIP_PACKET; - static const wstring QUICK_EQUIP_SERVER_PACKET; + //static const wstring QUICK_EQUIP_PACKET; + //static const wstring QUICK_EQUIP_SERVER_PACKET; static const wstring ENCHANTMENT_LIST_PACKET; diff --git a/Minecraft.World/DirtTile.cpp b/Minecraft.World/DirtTile.cpp index eb199945..00515129 100644 --- a/Minecraft.World/DirtTile.cpp +++ b/Minecraft.World/DirtTile.cpp @@ -42,8 +42,20 @@ Icon* DirtTile::getTexture(int face, int data) if (data < 0 || data >= DIRT_NAMES_LENGTH) data = 0; - if (TEXTURE_NAMES[data] == L"dirt_podzol") { - return (face == Facing::UP) ? podzolTop : podzolSide; + if (TEXTURE_NAMES[data] == L"dirt_podzol") + { + switch(face) + { + case Facing::UP: + return podzolTop; + break; + case Facing::DOWN: + return Tile::dirt->getTexture(face); + break; + default: + return podzolSide; + break; + } } return icons[data]; diff --git a/Minecraft.World/EnchantmentMenu.cpp b/Minecraft.World/EnchantmentMenu.cpp index 04cf5218..56505c00 100644 --- a/Minecraft.World/EnchantmentMenu.cpp +++ b/Minecraft.World/EnchantmentMenu.cpp @@ -6,13 +6,13 @@ #include "net.minecraft.world.item.h" #include "net.minecraft.world.item.enchantment.h" #include "EnchantmentMenu.h" -#include "../../../Minecraft.Client/ServerPlayer.h" -#include "../../../Minecraft.Client/MinecraftServer.h" -#include "../../../Minecraft.Client/PlayerList.h" -#include "../../../Minecraft.Client/MultiPlayerLocalPlayer.h" -#include "../../../Minecraft.Client/PlayerConnection.h" -#include "../../../Minecraft.World/CustomPayloadPacket.h" -#include "../../../Minecraft.Client/Minecraft.h" +#include "../Minecraft.Client/ServerPlayer.h" +#include "../Minecraft.Client/MinecraftServer.h" +#include "../Minecraft.Client/PlayerList.h" +#include "../Minecraft.Client/MultiPlayerLocalPlayer.h" +#include "../Minecraft.Client/PlayerConnection.h" +#include "../Minecraft.World/CustomPayloadPacket.h" +#include "../Minecraft.Client/Minecraft.h" EnchantmentMenu::EnchantmentMenu(shared_ptr inventory, Level *level, int xt, int yt, int zt) { diff --git a/Minecraft.World/FireworksRecipe.h b/Minecraft.World/FireworksRecipe.h index b0c7c236..015a2478 100644 --- a/Minecraft.World/FireworksRecipe.h +++ b/Minecraft.World/FireworksRecipe.h @@ -43,4 +43,8 @@ public: // 4J Added static void updatePossibleRecipes(shared_ptr craftSlots, bool *firework, bool *charge, bool *fade); static bool isValidIngredient(shared_ptr item, bool firework, bool charge, bool fade); + + virtual void writeToStream(DataOutputStream* dos) { + dos->writeByte(99); + } }; \ No newline at end of file diff --git a/Minecraft.World/ItemFrame.cpp b/Minecraft.World/ItemFrame.cpp index 497cbd6f..528c0711 100644 --- a/Minecraft.World/ItemFrame.cpp +++ b/Minecraft.World/ItemFrame.cpp @@ -10,7 +10,8 @@ #include "net.minecraft.world.level.saveddata.h" #include "com.mojang.nbt.h" #include "ItemFrame.h" - +#include "DamageSource.h" +#include "Level.h" @@ -87,29 +88,52 @@ shared_ptr ItemFrame::getItem() return getEntityData()->getItemInstance(DATA_ITEM); } -void ItemFrame::setItem(shared_ptr item) +void ItemFrame::setItem(shared_ptr item, bool notifyNeighbors) { - if(item != nullptr) - { - item = item->copy(); - item->count = 1; + if (item != nullptr) + { + item = item->copy(); + item->count = 1; + item->setFramed(dynamic_pointer_cast(shared_from_this())); + } + getEntityData()->set(DATA_ITEM, item); + getEntityData()->markDirty(DATA_ITEM); - item->setFramed(dynamic_pointer_cast( shared_from_this() )); - } - getEntityData()->set(DATA_ITEM, item); - getEntityData()->markDirty(DATA_ITEM); + if (notifyNeighbors) + { + level->updateNeighbourForOutputSignal(xTile, yTile, zTile, Tile::comparator_off->id); + } } -int ItemFrame::getRotation() +void ItemFrame::setItem(shared_ptr item) { - return getEntityData()->getByte(DATA_ROTATION); + setItem(item, true); } -void ItemFrame::setRotation(int rotation) +int ItemFrame::getRotation() { - getEntityData()->set(DATA_ROTATION, static_cast(rotation % 8)); + return getEntityData()->getByte(DATA_ROTATION); } +void ItemFrame::setRotation(int rotation, bool notifyNeighbors) +{ + getEntityData()->set(DATA_ROTATION, static_cast(rotation % 8)); + + if (notifyNeighbors) + { + level->updateNeighbourForOutputSignal(xTile, yTile, zTile, Tile::comparator_off->id); + } +} + +void ItemFrame::setRotation(int rotation) +{ + + getEntityData()->set(DATA_ROTATION, static_cast(rotation % 8)); + level->updateNeighbourForOutputSignal(xTile, yTile, zTile, Tile::comparator_off->id); +} + + + void ItemFrame::addAdditonalSaveData(CompoundTag *tag) { if (getItem() != nullptr) @@ -171,3 +195,54 @@ bool ItemFrame::interact(shared_ptr player) return true; } + +bool ItemFrame::hurt(DamageSource *source, float damage) +{ + if (level->isClientSide) return false; + + shared_ptr item = getItem(); + + if (!source->isExplosion() && item != nullptr) + { + shared_ptr sourceEntity = source->getEntity(); + + if (sourceEntity != nullptr && sourceEntity->instanceof(eTYPE_PLAYER)) + { + shared_ptr player = dynamic_pointer_cast(sourceEntity); + if (!player->abilities.instabuild) + { + shared_ptr copy = item->copy(); + removeFramedMap(copy); + spawnAtLocation(copy, 0); + } + else + { + removeFramedMap(item); + } + } + else + { + shared_ptr copy = item->copy(); + removeFramedMap(copy); + spawnAtLocation(copy, 0); + } + + setItem(nullptr); + return true; + } + + return HangingEntity::hurt(source, damage); +} + +int ItemFrame::getAnalogOutput() +{ + shared_ptr item = getItem(); + if (item == nullptr) return 0; + return getRotation() % 8 + 1; +} + + +float ItemFrame::getPickRadius() +{ + return 0.0f; +} \ No newline at end of file diff --git a/Minecraft.World/ItemFrame.h b/Minecraft.World/ItemFrame.h index 06c1f111..dc83505f 100644 --- a/Minecraft.World/ItemFrame.h +++ b/Minecraft.World/ItemFrame.h @@ -38,9 +38,14 @@ private: public: shared_ptr getItem(); + void setItem(shared_ptr item, bool notifyNeighbors); void setItem(shared_ptr item); int getRotation(); + void setRotation(int rotation, bool notifyNeighbors); void setRotation(int rotation); + virtual bool hurt(DamageSource *source, float damage) override; + virtual int getAnalogOutput(); + virtual float getPickRadius()override; virtual void addAdditonalSaveData(CompoundTag *tag); virtual void readAdditionalSaveData(CompoundTag *tag); diff --git a/Minecraft.World/ItemInstance.cpp b/Minecraft.World/ItemInstance.cpp index 9478057a..43d1cce9 100644 --- a/Minecraft.World/ItemInstance.cpp +++ b/Minecraft.World/ItemInstance.cpp @@ -396,6 +396,11 @@ void ItemInstance::setAuxValue(int value) } } +void ItemInstance::setRawAuxValue(int value) +{ + auxValue = value; +} + int ItemInstance::getMaxDamage() { return Item::items[id]->getMaxDamage(); diff --git a/Minecraft.World/ItemInstance.h b/Minecraft.World/ItemInstance.h index cff33a97..e2b0e059 100644 --- a/Minecraft.World/ItemInstance.h +++ b/Minecraft.World/ItemInstance.h @@ -90,6 +90,7 @@ public: int getDamageValue(); int getAuxValue() const; void setAuxValue(int value); + void setRawAuxValue(int value); int getMaxDamage(); bool hurt(int dmg, Random *random); void hurtAndBreak(int dmg, shared_ptr owner); diff --git a/Minecraft.World/LeafTile2.cpp b/Minecraft.World/LeafTile2.cpp index 319ff1b8..bff00322 100644 --- a/Minecraft.World/LeafTile2.cpp +++ b/Minecraft.World/LeafTile2.cpp @@ -15,13 +15,13 @@ const unsigned int LeafTile2::LEAF2_NAMES[LEAF2_NAMES_SIZE] = { }; const wstring LeafTile2::TEXTURES[2][2] = { - { L"leaves_acacia", L"leaves_dark_oak" }, // Indice 0: Fancy - { L"leaves_acacia_opaque", L"leaves_dark_oak_opaque" } // Indice 1: Veloce/Opaca + { L"leaves_acacia", L"leaves_dark_oak" }, // index 0: Fancy + { L"leaves_acacia_opaque", L"leaves_dark_oak_opaque" } // index 1: Fast }; LeafTile2::LeafTile2(int id) : LeafTile(id) { - // Non serve fare checkBuffer qui, ci pensa già la classe padre LeafTile! + // do nothing here } Icon *LeafTile2::getTexture(int face, int data) @@ -29,8 +29,8 @@ Icon *LeafTile2::getTexture(int face, int data) int type = data & 3; if (type >= LEAF2_NAMES_SIZE) type = 0; - // isSolidRender() in LeafTile restituisce 'true' se la grafica è su Veloce/Opaca. - // Quindi se è true usiamo l'indice 1, se è false (Trasparente) usiamo l'indice 0. + // isSolidRender() in LeafTile returns 'true' if graphics is Fast + // if true -> index is 1, else 0. int textureSet = isSolidRender(false) ? 1 : 0; return icons[textureSet][type]; @@ -56,13 +56,14 @@ void LeafTile2::registerIcons(IconRegister *iconRegister) int LeafTile2::getColor(int data) { - // In inventario o in mano, l'Acacia e la Dark Oak usano il verde base + // in the inventory use the default colour for leaves return FoliageColor::getDefaultColor(); } int LeafTile2::getColor(LevelSource *level, int x, int y, int z, int data) { - // Codice di blending per il colore del bioma (copiato dal tuo LeafTile.cpp) + // Codice di blending per il colore del bioma (copiato dal tuo LeafTile.cpp)) + // blending biome colors copied from LeafTile.cpp int totalRed = 0; int totalGreen = 0; int totalBlue = 0; @@ -71,7 +72,7 @@ int LeafTile2::getColor(LevelSource *level, int x, int y, int z, int data) { for (int ox = -1; ox <= 1; ox++) { - int foliageColor = level->getBiome(x + ox, z + oz)->getFolageColor(); // Attento, nel tuo engine si chiama getFolageColor() senza la 'i' + int foliageColor = level->getBiome(x + ox, z + oz)->getFolageColor(); // they mispelled the word. getFolageColor without "i" totalRed += (foliageColor & 0xff0000) >> 16; totalGreen += (foliageColor & 0xff00) >> 8; totalBlue += (foliageColor & 0xff); @@ -83,7 +84,7 @@ int LeafTile2::getColor(LevelSource *level, int x, int y, int z, int data) void LeafTile2::playerDestroy(Level *level, shared_ptr player, int x, int y, int z, int data) { - // Se il giocatore usa le cesoie, vogliamo droppare "leaves2" (ID 161) e non "leaves" (ID 18) + // if player is using shears, drop "leaves2" (ID 161) , instead of "leaves" (ID 18) if (!level->isClientSide && player->getSelectedItem() != nullptr && player->getSelectedItem()->id == Item::shears->id) { player->awardStat( @@ -95,7 +96,7 @@ void LeafTile2::playerDestroy(Level *level, shared_ptr player, int x, in } else { - // Altrimenti usa la distruzione standard di TransparentTile + // or default destroy TransparentTile::playerDestroy(level, player, x, y, z, data); } } diff --git a/Minecraft.World/LeafTile2.h b/Minecraft.World/LeafTile2.h index 59166c46..d3bc3c81 100644 --- a/Minecraft.World/LeafTile2.h +++ b/Minecraft.World/LeafTile2.h @@ -13,7 +13,7 @@ public: static const unsigned int LEAF2_NAMES[LEAF2_NAMES_SIZE]; private: - //[0] = Fancy (Trasparenti), [1] = Fast (Opache) + //index 0, fancy; index 1, fast static const wstring TEXTURES[2][2]; Icon *icons[2][2]; diff --git a/Minecraft.World/Level.cpp b/Minecraft.World/Level.cpp index 7cd3eb88..ffce38b7 100644 --- a/Minecraft.World/Level.cpp +++ b/Minecraft.World/Level.cpp @@ -46,6 +46,7 @@ DWORD Level::tlsIdxLightCache = TlsAlloc(); // 4J : WESTY : Added for time played stats. #include "net.minecraft.stats.h" +#include "../Minecraft.Client/MultiPlayerChunkCache.h" // 4J - Caching of lighting data added. This is implemented as a 16x16x16 cache of ints (ie 16K storage in total). The index of the element to be used in the array is determined by the lower // four bits of each x/y/z position, and the upper 7/4/7 bits of the x/y/z positions are stored within the element itself along with the cached values etc. The cache can be enabled per thread by @@ -1334,10 +1335,10 @@ int Level::getBrightness(LightLayer::variety layer, int x, int y, int z) if( ( ix < 0 ) || ( ix >= chunkSourceXZSize ) ) return 0; if( ( iz < 0 ) || ( iz >= chunkSourceXZSize ) ) return 0; - int idx = ix * chunkSourceXZSize + iz; + int idx = MultiPlayerChunkCache::wrapCoord(ix, LEVEL_MIN_WIDTH) * LEVEL_MIN_WIDTH + MultiPlayerChunkCache::wrapCoord(iz, LEVEL_MIN_WIDTH); LevelChunk *c = chunkSourceCache[idx]; - if( c == nullptr ) return (int)layer; + if( c == nullptr) return (int)layer; if (y < 0) y = 0; if (y >= maxBuildHeight) y = maxBuildHeight - 1; @@ -1383,7 +1384,7 @@ void Level::getNeighbourBrightnesses(int *brightnesses, LightLayer::variety laye return; } - int idx = ix * chunkSourceXZSize + iz; + int idx = MultiPlayerChunkCache::wrapCoord(ix, LEVEL_MIN_WIDTH) * LEVEL_MIN_WIDTH + MultiPlayerChunkCache::wrapCoord(iz, LEVEL_MIN_WIDTH); LevelChunk *c = chunkSourceCache[idx]; // 4J Stu - The java LightLayer was an enum class type with a member "surrounding" which is what we diff --git a/Minecraft.World/LevelData.cpp b/Minecraft.World/LevelData.cpp index 0edd2d7e..e9f22fdb 100644 --- a/Minecraft.World/LevelData.cpp +++ b/Minecraft.World/LevelData.cpp @@ -185,6 +185,7 @@ LevelData::LevelData(CompoundTag *tag) case LEVEL_WIDTH_SMALL: hostOptionworldSize = e_worldSize_Small; break; case LEVEL_WIDTH_MEDIUM: hostOptionworldSize = e_worldSize_Medium; break; case LEVEL_WIDTH_LARGE: hostOptionworldSize = e_worldSize_Large; break; + case LEVEL_WIDTH_EXPANDED: hostOptionworldSize = e_worldSize_Expanded; break; default: assert(0); break; } app.SetGameHostOption(eGameHostOption_WorldSize, hostOptionworldSize ); diff --git a/Minecraft.World/MobEffect.cpp b/Minecraft.World/MobEffect.cpp index 71620b0c..6ca532a7 100644 --- a/Minecraft.World/MobEffect.cpp +++ b/Minecraft.World/MobEffect.cpp @@ -137,9 +137,13 @@ void MobEffect::applyEffectTick(shared_ptr mob, int amplification) } else if (id == poison->id) { - if (mob->getHealth() > 1.0f) + // poison must never reduce health below 1 hp + // if the current health is between 1 and 2 hp the player is left at exactly 1 HP rather than dying. + float currentHealth = mob->getHealth(); + if (currentHealth > 1.0f) { - mob->hurt(DamageSource::magic, 1.0f); + float poisonDmg = min(1.0f, currentHealth - 1.0f); + mob->hurt(DamageSource::magic, poisonDmg); } } else if (id == wither->id) diff --git a/Minecraft.World/MoveEntityPacket.cpp b/Minecraft.World/MoveEntityPacket.cpp index a2b79db3..f071a49f 100644 --- a/Minecraft.World/MoveEntityPacket.cpp +++ b/Minecraft.World/MoveEntityPacket.cpp @@ -30,7 +30,7 @@ MoveEntityPacket::MoveEntityPacket(int id) void MoveEntityPacket::read(DataInputStream *dis) //throws IOException { - id = dis->readShort(); + id = dis->readInt(); } void MoveEntityPacket::write(DataOutputStream *dos) //throws IOException @@ -40,7 +40,7 @@ void MoveEntityPacket::write(DataOutputStream *dos) //throws IOException // We shouln't be tracking an entity that doesn't have a short type of id DEBUG_BREAK(); } - dos->writeShort(static_cast(id)); + dos->writeInt(static_cast(id)); } void MoveEntityPacket::handle(PacketListener *listener) @@ -50,7 +50,7 @@ void MoveEntityPacket::handle(PacketListener *listener) int MoveEntityPacket::getEstimatedSize() { - return 2; + return 4; } bool MoveEntityPacket::canBeInvalidated() @@ -101,7 +101,7 @@ void MoveEntityPacket::PosRot::write(DataOutputStream *dos) //throws IOException int MoveEntityPacket::PosRot::getEstimatedSize() { - return 2+5; + return 4+5; } MoveEntityPacket::Pos::Pos() @@ -133,7 +133,7 @@ void MoveEntityPacket::Pos::write(DataOutputStream *dos) //throws IOException int MoveEntityPacket::Pos::getEstimatedSize() { - return 2+3; + return 4+3; } MoveEntityPacket::Rot::Rot() @@ -164,5 +164,5 @@ void MoveEntityPacket::Rot::write(DataOutputStream *dos) //throws IOException int MoveEntityPacket::Rot::getEstimatedSize() { - return 2+2; + return 4+2; } diff --git a/Minecraft.World/MoveEntityPacketSmall.cpp b/Minecraft.World/MoveEntityPacketSmall.cpp index 3e1b922c..2f6d3bc2 100644 --- a/Minecraft.World/MoveEntityPacketSmall.cpp +++ b/Minecraft.World/MoveEntityPacketSmall.cpp @@ -37,7 +37,7 @@ MoveEntityPacketSmall::MoveEntityPacketSmall(int id) void MoveEntityPacketSmall::read(DataInputStream *dis) //throws IOException { - id = dis->readShort(); + id = dis->readInt(); } void MoveEntityPacketSmall::write(DataOutputStream *dos) //throws IOException @@ -47,7 +47,7 @@ void MoveEntityPacketSmall::write(DataOutputStream *dos) //throws IOException // We shouln't be tracking an entity that doesn't have a short type of id DEBUG_BREAK(); } - dos->writeShort(static_cast(id)); + dos->writeInt(id); } void MoveEntityPacketSmall::handle(PacketListener *listener) @@ -57,7 +57,7 @@ void MoveEntityPacketSmall::handle(PacketListener *listener) int MoveEntityPacketSmall::getEstimatedSize() { - return 2; + return 4; } bool MoveEntityPacketSmall::canBeInvalidated() @@ -88,13 +88,12 @@ MoveEntityPacketSmall::PosRot::PosRot(int id, char xa, char ya, char za, char yR void MoveEntityPacketSmall::PosRot::read(DataInputStream *dis) //throws IOException { - int idAndRot = dis->readShort(); - this->id = idAndRot & 0x07ff; - this->yRot = idAndRot >> 11; - int xAndYAndZ = (int)dis->readShort(); - this->xa = xAndYAndZ >> 11; - this->ya = (xAndYAndZ << 21 ) >> 26; - this->za = (xAndYAndZ << 27 ) >> 27; + this->id = dis->readInt(); + this->yRot = dis->readChar(); + int XandYandZ = (int)dis->readShort(); + this->xa = XandYandZ >> 11; + this->ya = (XandYandZ << 21 ) >> 26; + this->za = (XandYandZ << 27 ) >> 27; } void MoveEntityPacketSmall::PosRot::write(DataOutputStream *dos) //throws IOException @@ -104,15 +103,15 @@ void MoveEntityPacketSmall::PosRot::write(DataOutputStream *dos) //throws IOExce // We shouln't be tracking an entity that doesn't have a short type of id DEBUG_BREAK(); } - short idAndRot = id | yRot << 11; - dos->writeShort(idAndRot); - short xAndYAndZ = ( xa << 11 ) | ( ( ya & 0x3f ) << 5 ) | ( za & 0x1f ); - dos->writeShort(xAndYAndZ); + dos->writeInt(id); + dos->writeChar(yRot); + short XandYandZ = ( xa << 11 ) | ( ( ya & 0x3f ) << 5 ) | ( za & 0x1f ); + dos->writeShort(XandYandZ); } int MoveEntityPacketSmall::PosRot::getEstimatedSize() { - return 4; + return 7; } MoveEntityPacketSmall::Pos::Pos() @@ -128,9 +127,8 @@ MoveEntityPacketSmall::Pos::Pos(int id, char xa, char ya, char za) : MoveEntityP void MoveEntityPacketSmall::Pos::read(DataInputStream *dis) //throws IOException { - int idAndY = dis->readShort(); - this->id = idAndY & 0x07ff; - this->ya = idAndY >> 11; + this->id = dis->readInt(); + this->ya = dis->readChar(); int XandZ = (int)static_cast(dis->readByte()); xa = XandZ >> 4; za = ( XandZ << 28 ) >> 28; @@ -143,15 +141,15 @@ void MoveEntityPacketSmall::Pos::write(DataOutputStream *dos) //throws IOExcepti // We shouln't be tracking an entity that doesn't have a short type of id DEBUG_BREAK(); } - short idAndY = id | ya << 11; - dos->writeShort(idAndY); + dos->writeInt(id); + dos->writeChar(ya); char XandZ = ( xa << 4 ) | ( za & 0x0f ); dos->writeByte(XandZ); } int MoveEntityPacketSmall::Pos::getEstimatedSize() { - return 3; + return 7; } MoveEntityPacketSmall::Rot::Rot() @@ -169,9 +167,8 @@ MoveEntityPacketSmall::Rot::Rot(int id, char yRot, char xRot) : MoveEntityPacket void MoveEntityPacketSmall::Rot::read(DataInputStream *dis) //throws IOException { - int idAndRot = (int)dis->readShort(); - this->id = idAndRot & 0x07ff; - this->yRot = idAndRot >> 11; + this->id = dis->readInt(); + this->yRot = dis->readChar(); } void MoveEntityPacketSmall::Rot::write(DataOutputStream *dos) //throws IOException @@ -181,11 +178,11 @@ void MoveEntityPacketSmall::Rot::write(DataOutputStream *dos) //throws IOExcepti // We shouln't be tracking an entity that doesn't have a short type of id DEBUG_BREAK(); } - short idAndRot = id | yRot << 11; - dos->writeShort(idAndRot); + dos->writeInt(id); + dos->writeChar(yRot); } int MoveEntityPacketSmall::Rot::getEstimatedSize() { - return 2; + return 5; } diff --git a/Minecraft.World/Packet.h b/Minecraft.World/Packet.h index c639c8f1..9938afa6 100644 --- a/Minecraft.World/Packet.h +++ b/Minecraft.World/Packet.h @@ -103,7 +103,5 @@ public: static shared_ptr readItem(DataInputStream *dis); static void writeItem(shared_ptr item, DataOutputStream *dos); static CompoundTag *readNbt(DataInputStream *dis); - -protected: static void writeNbt(CompoundTag *tag, DataOutputStream *dos); }; \ No newline at end of file diff --git a/Minecraft.World/Recipes.cpp b/Minecraft.World/Recipes.cpp index 4bc9a397..b2eb70e2 100644 --- a/Minecraft.World/Recipes.cpp +++ b/Minecraft.World/Recipes.cpp @@ -29,20 +29,14 @@ void Recipes::_init() { // 4J Jev: instance = new Recipes(); recipies = new RecipyList(); -} -Recipes::Recipes() -{ - int iCount=0; - _init(); - - pArmorRecipes = new ArmorRecipes; - pClothDyeRecipes = new ClothDyeRecipes; - pFoodRecipies = new FoodRecipies; - pOreRecipies = new OreRecipies; - pStructureRecipies = new StructureRecipies; - pToolRecipies = new ToolRecipies; - pWeaponRecipies = new WeaponRecipies; + pArmorRecipes = new ArmorRecipes; + pClothDyeRecipes = new ClothDyeRecipes; + pFoodRecipies = new FoodRecipies; + pOreRecipies = new OreRecipies; + pStructureRecipies = new StructureRecipies; + pToolRecipies = new ToolRecipies; + pWeaponRecipies = new WeaponRecipies; // 4J Stu - These just don't work with our crafting menu //recipies->push_back(new ArmorDyeRecipe()); @@ -50,8 +44,10 @@ Recipes::Recipes() //recipies->add(new MapExtendingRecipe()); //recipies->add(new FireworksRecipe()); pFireworksRecipes = new FireworksRecipe(); +} - +void Recipes::_compileRecipes() +{ addShapedRecipy(new ItemInstance(Tile::wood, 4, 0), // L"sczg", L"#", // @@ -186,7 +182,7 @@ Recipes::Recipes() L"W#W", // L"W#W", // - L'#', Item::stick, + L'#', Item::stick, L'W', new ItemInstance(Tile::wood, 1, TreeTile::ACACIA_TRUNK), L'S'); @@ -195,7 +191,7 @@ Recipes::Recipes() L"W#W", // L"W#W", // - L'#', Item::stick, + L'#', Item::stick, L'W', new ItemInstance(Tile::wood, 1, TreeTile::DARK_TRUNK), L'S'); @@ -475,12 +471,10 @@ Recipes::Recipes() L'S'); pArmorRecipes->addRecipes(this); - //iCount=getRecipies()->size(); + pClothDyeRecipes->addRecipes(this); - - addShapedRecipy(new ItemInstance(Tile::snow, 1), // L"sscig", L"##", // @@ -497,7 +491,7 @@ Recipes::Recipes() L'#', Item::prismarine_shard, L'S'); - addShapedRecipy(new ItemInstance(Tile::prismarine, 1,PrismarineTile::TYPE_BRICKS), // + addShapedRecipy(new ItemInstance(Tile::prismarine, 1, PrismarineTile::TYPE_BRICKS), // L"ssscig", L"###", // L"###", // @@ -507,7 +501,7 @@ Recipes::Recipes() L'S'); - addShapedRecipy(new ItemInstance(Tile::prismarine, 1,PrismarineTile::TYPE_DARK), // + addShapedRecipy(new ItemInstance(Tile::prismarine, 1, PrismarineTile::TYPE_DARK), // L"ssscicig", L"###", // L"#X#", // @@ -657,10 +651,6 @@ Recipes::Recipes() - - - //iCount=getRecipies()->size(); - addShapedRecipy(new ItemInstance(Item::cake, 1), // L"ssscicicicig", L"AAA", // @@ -803,7 +793,7 @@ Recipes::Recipes() L'F'); // Moved bow and arrow in from weapons to avoid stacking on the group name display - addShapedRecipy(new ItemInstance((Item *)Item::bow, 1), // + addShapedRecipy(new ItemInstance((Item*)Item::bow, 1), // L"ssscicig", L" #X", // L"# X", // @@ -850,7 +840,7 @@ Recipes::Recipes() L'#', Tile::glass, L'T'); - + // torch made of charcoal - moved to be the default due to the tutorial using it addShapedRecipy(new ItemInstance(Tile::torch, 4), // @@ -1096,24 +1086,24 @@ Recipes::Recipes() L'#', Tile::wood, L'X', Item::diamond, 'D'); - - addShapedRecipy(new ItemInstance(Item::leather, 1), - L"sscig", - L"##", - L"##", - L'#', Item::rabbit_hide, - L'D'); + addShapedRecipy(new ItemInstance(Item::leather, 1), + L"sscig", + L"##", + L"##", + + L'#', Item::rabbit_hide, + L'D'); addShapedRecipy(new ItemInstance(Item::armor_stand, 1), - L"ssscictg", - L"SSS", - L" S ", - L"SXS", - L'S', Item::stick, - L'X', Tile::stoneSlabHalf, + L"ssscictg", + L"SSS", + L" S ", + L"SXS", + L'S', Item::stick, + L'X', Tile::stoneSlabHalf, L'D'); - + addShapedRecipy(new ItemInstance(Item::paper, 3), // @@ -1206,19 +1196,19 @@ Recipes::Recipes() L'D'); // 4J - TODO - put these new 1.7.3 items in required place within recipes - addShapedRecipy(new ItemInstance(static_cast(Tile::pistonBase), 1), // + addShapedRecipy(new ItemInstance(static_cast(Tile::pistonBase), 1), // L"sssctcicictg", - L"TTT", // - L"#X#", // - L"#R#", // + L"TTT", // + L"#X#", // + L"#R#", // L'#', Tile::cobblestone, L'X', Item::iron_ingot, L'R', Item::redstone, L'T', Tile::wood, L'M'); addShapedRecipy(new ItemInstance(static_cast(Tile::sticky_piston), 1), // L"sscictg", - L"S", // - L"P", // + L"S", // + L"P", // L'S', Item::slime_ball, L'P', Tile::pistonBase, L'M'); @@ -1249,37 +1239,38 @@ Recipes::Recipes() L'D', Item::dye, L'C', Item::firework_charge, L'D'); - - // Sort so the largest recipes get checked first! - /* 4J-PB - TODO - Collections.sort(recipies, new Comparator() - { - public: int compare(Recipy r0, Recipy r1) - { - - // shapeless recipes are put in the back of the list - if (r0 instanceof ShapelessRecipy && r1 instanceof ShapedRecipy) - { - return 1; - } - if (r1 instanceof ShapelessRecipy && r0 instanceof ShapedRecipy) - { - return -1; - } - - if (r1.size() < r0.size()) return -1; - if (r1.size() > r0.size()) return 1; - return 0; - } - }); - */ - - // 4J-PB removed System.out.println(recipies->size() + L" recipes"); - - // 4J-PB - build the array of ingredients required per recipe buildRecipeIngredientsArray(); } +void Recipes::_wipeRecipes() +{ + int iCount = recipies->size(); + + for (int i = 0; i < iCount; i++) { + Recipy::INGREDIENTS_REQUIRED& req = m_pRecipeIngredientsRequired[i]; + + delete[] req.iIngIDA; + delete[] req.iIngValA; + delete[] req.iIngAuxValA; + delete[] req.uiGridA; + } + + for (int i = 0; i < iCount; i++) { + delete (*recipies)[i]; + } + + recipies->clear(); + + delete[] m_pRecipeIngredientsRequired; + m_pRecipeIngredientsRequired = nullptr; +} + +Recipes::Recipes() +{ + _init(); + _compileRecipes(); +} + // 4J-PB - this function has been substantially changed due to the differences with a va_list of classes in C++ and Java ShapedRecipy *Recipes::addShapedRecipy(ItemInstance *result, ...) { @@ -1563,7 +1554,7 @@ void Recipes::buildRecipeIngredientsArray(void) int iRecipeC=static_cast(recipies->size()); - m_pRecipeIngredientsRequired= new Recipy::INGREDIENTS_REQUIRED [iRecipeC]; + m_pRecipeIngredientsRequired = new Recipy::INGREDIENTS_REQUIRED [iRecipeC]; int iCount=0; for (auto& recipe : *recipies) @@ -1577,4 +1568,46 @@ void Recipes::buildRecipeIngredientsArray(void) Recipy::INGREDIENTS_REQUIRED *Recipes::getRecipeIngredientsArray(void) { return m_pRecipeIngredientsRequired; -} \ No newline at end of file +} + +void Recipes::loadFromLocal() +{ + this->_wipeRecipes(); + this->_compileRecipes(); +} + +void Recipes::loadFromPacket(byteArray packetData) +{ + ByteArrayInputStream bais(packetData); + DataInputStream input(&bais); + + this->_wipeRecipes(); + { + int iCount = input.readInt(); + for (int i = 0; i < iCount; i++) { + int recipeType = input.readByte(); + + if (recipeType == 1) { + recipies->push_back(ShapelessRecipy::readFromStream(&input)); + } else if (recipeType == 2) { + recipies->push_back(ShapedRecipy::readFromStream(&input)); + } + } + } + this->buildRecipeIngredientsArray(); +} + +std::shared_ptr Recipes::createUpdatePacket() +{ + ByteArrayOutputStream baos; + DataOutputStream dos(&baos); + + int iCount = recipies->size(); + dos.writeInt(iCount); + + for (int i = 0; i < iCount; i++) { + (*recipies)[i]->writeToStream(&dos); + } + + return std::make_shared(CustomPayloadPacket::UPDATE_RECIPE_REGISTRY, baos.toByteArray()); +} diff --git a/Minecraft.World/Recipes.h b/Minecraft.World/Recipes.h index d6e508a8..856e3a64 100644 --- a/Minecraft.World/Recipes.h +++ b/Minecraft.World/Recipes.h @@ -16,6 +16,7 @@ import net.minecraft.world.level.tile.Tile; */ #include "Recipy.h" +#include "../Minecraft.World/CustomPayloadPacket.h" #pragma once using namespace std; @@ -84,6 +85,8 @@ public: private: void _init(); // 4J add + void _compileRecipes(); + void _wipeRecipes(); Recipes(); public: @@ -97,6 +100,11 @@ public: shared_ptr getItemForRecipe(Recipy *r); Recipy::INGREDIENTS_REQUIRED *getRecipeIngredientsArray(); + void loadFromLocal(); + void loadFromPacket(byteArray packetData); + + std::shared_ptr createUpdatePacket(); + private: void buildRecipeIngredientsArray(); Recipy::INGREDIENTS_REQUIRED *m_pRecipeIngredientsRequired; diff --git a/Minecraft.World/Recipy.h b/Minecraft.World/Recipy.h index 9cbf5cac..b2ad0127 100644 --- a/Minecraft.World/Recipy.h +++ b/Minecraft.World/Recipy.h @@ -10,13 +10,13 @@ #define RECIPE_TYPE_2x2 0 #define RECIPE_TYPE_3x3 1 -class Recipy +class Recipy { public: enum _eGroupType { - eGroupType_First=0, - eGroupType_Structure=0, + eGroupType_First = 0, + eGroupType_Structure = 0, eGroupType_Tool, eGroupType_Food, eGroupType_Armour, @@ -28,28 +28,30 @@ public: eGroupType; // to class the item produced by the recipe // 4J-PB - we'll classing an ingredient ID with a different aux value as a different IngID AuxVal pair - typedef struct + typedef struct { int iIngC; int iType; // Can be a 2x2 or a 3x3. Inventory crafting can only make a 2x2. - int *iIngIDA; - int *iIngValA; - int *iIngAuxValA; - Recipy *pRecipy; + int* iIngIDA; + int* iIngValA; + int* iIngAuxValA; + Recipy* pRecipy; bool bCanMake[XUSER_MAX_COUNT]; - unsigned int *uiGridA; // hold the layout of the recipe (id | auxval<<24) + unsigned int* uiGridA; // hold the layout of the recipe (id | auxval<<24) unsigned short usBitmaskMissingGridIngredients[XUSER_MAX_COUNT]; // each bit set means we don't have that grid ingredient } INGREDIENTS_REQUIRED; - ~Recipy() {} - virtual bool matches(shared_ptr craftSlots, Level *level) = 0; + virtual ~Recipy() = default; + virtual bool matches(shared_ptr craftSlots, Level* level) = 0; virtual shared_ptr assemble(shared_ptr craftSlots) = 0; virtual int size() = 0; - virtual const ItemInstance *getResultItem() = 0; - virtual const int getGroup() = 0; + virtual const ItemInstance* getResultItem() = 0; + virtual const int getGroup() = 0; // 4J-PB virtual bool reqs(int iRecipe) = 0; - virtual void reqs(INGREDIENTS_REQUIRED *pIngReq) = 0; + virtual void reqs(INGREDIENTS_REQUIRED* pIngReq) = 0; + + virtual void writeToStream(DataOutputStream* dos) = 0; }; diff --git a/Minecraft.World/RespawnPacket.cpp b/Minecraft.World/RespawnPacket.cpp index c4a19c8c..aee7d729 100644 --- a/Minecraft.World/RespawnPacket.cpp +++ b/Minecraft.World/RespawnPacket.cpp @@ -58,7 +58,7 @@ void RespawnPacket::read(DataInputStream *dis) //throws IOException mapSeed = dis->readLong(); difficulty = dis->readByte(); m_newSeaLevel = dis->readBoolean(); - m_newEntityId = dis->readShort(); + m_newEntityId = dis->readInt(); #ifdef _LARGE_WORLDS m_xzSize = dis->readShort(); m_hellScale = dis->read(); @@ -83,7 +83,7 @@ void RespawnPacket::write(DataOutputStream *dos) //throws IOException dos->writeLong(mapSeed); dos->writeByte(difficulty); dos->writeBoolean(m_newSeaLevel); - dos->writeShort(m_newEntityId); + dos->writeInt(m_newEntityId); #ifdef _LARGE_WORLDS dos->writeShort(m_xzSize); dos->write(m_hellScale); @@ -97,5 +97,5 @@ int RespawnPacket::getEstimatedSize() { length = static_cast(m_pLevelType->getGeneratorName().length()); } - return 13+length; + return 13+length+2; } diff --git a/Minecraft.World/SetEntityMotionPacket.cpp b/Minecraft.World/SetEntityMotionPacket.cpp index 20d841f1..8488eee7 100644 --- a/Minecraft.World/SetEntityMotionPacket.cpp +++ b/Minecraft.World/SetEntityMotionPacket.cpp @@ -49,9 +49,9 @@ SetEntityMotionPacket::SetEntityMotionPacket(int id, double xd, double yd, doubl void SetEntityMotionPacket::read(DataInputStream *dis) //throws IOException { - short idAndFlag = dis->readShort(); - id = idAndFlag & 0x07ff; - if( idAndFlag & 0x0800 ) + useBytes = dis->readBoolean(); + id = dis->readInt(); + if(useBytes) { xa = static_cast(dis->readByte()); ya = static_cast(dis->readByte()); @@ -62,29 +62,28 @@ void SetEntityMotionPacket::read(DataInputStream *dis) //throws IOException xa *= 16; ya *= 16; za *= 16; - useBytes = true; } else { xa = dis->readShort(); ya = dis->readShort(); za = dis->readShort(); - useBytes = false; } } void SetEntityMotionPacket::write(DataOutputStream *dos) //throws IOException { + dos->writeBoolean(useBytes); if( useBytes ) { - dos->writeShort(id | 0x800); + dos->writeInt(id); dos->writeByte(xa/16); dos->writeByte(ya/16); dos->writeByte(za/16); } else { - dos->writeShort(id); + dos->writeInt(id); dos->writeShort(xa); dos->writeShort(ya); dos->writeShort(za); @@ -98,7 +97,7 @@ void SetEntityMotionPacket::handle(PacketListener *listener) int SetEntityMotionPacket::getEstimatedSize() { - return useBytes ? 5 : 8; + return useBytes ? 8 : 11; } bool SetEntityMotionPacket::canBeInvalidated() diff --git a/Minecraft.World/ShapedRecipy.cpp b/Minecraft.World/ShapedRecipy.cpp index 7a0e85b0..d73fe6ee 100644 --- a/Minecraft.World/ShapedRecipy.cpp +++ b/Minecraft.World/ShapedRecipy.cpp @@ -23,6 +23,24 @@ ShapedRecipy::ShapedRecipy(int width, int height, ItemInstance **recipeItems, It _keepTag = false; } +ShapedRecipy::~ShapedRecipy() { + // todo: why does this cause a error when clearing out these specifically? + // might be leaking memory here but im not sure cause it crashes when you clear them, so we dont clear them + /*for (int x = 0; x < 3; x++) { + for (int y = 0; y < 3; y++) { + if (x < width && y < height) { + delete recipeItems[x + y * width]; + } + } + }*/ + + delete[] recipeItems; + delete result; + + recipeItems = nullptr; + result = nullptr; +} + const int ShapedRecipy::getGroup() { return group; @@ -227,4 +245,65 @@ ShapedRecipy *ShapedRecipy::keepTag() { _keepTag = true; return this; +} + +void ShapedRecipy::writeToStream(DataOutputStream* dos) { + dos->writeByte(2); + dos->writeByte(this->group); + + //write result item, it should always be valid + { + dos->writeShort(this->result->id); + dos->writeByte(this->result->count); + dos->writeShort(this->result->getAuxValue()); + + Packet::writeNbt(this->result->tag, dos); + } + + dos->writeByte((this->width << 2) | this->height); + + for (int i = 0; i < (this->width * this->height); i++) { + ItemInstance* ingredients_item = this->recipeItems[i]; + dos->writeBoolean(ingredients_item == nullptr); + if (ingredients_item == nullptr) continue; + + dos->writeShort(ingredients_item->id); + dos->writeShort(ingredients_item->getAuxValue()); + Packet::writeNbt(ingredients_item->tag, dos); + } +} + +ShapedRecipy* ShapedRecipy::readFromStream(DataInputStream* dis) { + int groupType = dis->readByte(); + + int resultItemID = dis->readShort(); + int resultItemCount = dis->readByte(); + int resultItemAux = dis->readShort(); + + ItemInstance* resultItem = new ItemInstance(resultItemID, resultItemCount, 0); + resultItem->setRawAuxValue(resultItemAux); + resultItem->tag = Packet::readNbt(dis); + + unsigned char packedSize = dis->readByte(); + + int width = (packedSize >> 2) & 0x3; + int height = packedSize & 0x3; + ItemInstance** ids = new ItemInstance*[width * height]; + + for (int i = 0; i < width * height; i++) { + ItemInstance* ingredients_item = nullptr; + bool isNull = dis->readBoolean(); + if (!isNull) { + int itemId = dis->readShort(); + int itemAux = dis->readShort(); + + ingredients_item = new ItemInstance(itemId, 1, 0); + ingredients_item->setRawAuxValue(itemAux); + ingredients_item->tag = Packet::readNbt(dis); + } + + ids[i] = ingredients_item; + } + + return new ShapedRecipy(width, height, ids, resultItem, groupType); } \ No newline at end of file diff --git a/Minecraft.World/ShapedRecipy.h b/Minecraft.World/ShapedRecipy.h index c111b7c7..25b6cc29 100644 --- a/Minecraft.World/ShapedRecipy.h +++ b/Minecraft.World/ShapedRecipy.h @@ -1,21 +1,22 @@ #pragma once -class ShapedRecipy : public Recipy +class ShapedRecipy : public Recipy { private: int width, height, group; - ItemInstance **recipeItems; - ItemInstance *result; + ItemInstance** recipeItems; + ItemInstance* result; bool _keepTag; public: const int resultId; public: - ShapedRecipy(int width, int height, ItemInstance **recipeItems, ItemInstance *result, int iGroup=Recipy::eGroupType_Decoration); + ShapedRecipy(int width, int height, ItemInstance** recipeItems, ItemInstance* result, int iGroup = Recipy::eGroupType_Decoration); + virtual ~ShapedRecipy() override; - virtual const ItemInstance *getResultItem(); + virtual const ItemInstance* getResultItem(); virtual const int getGroup(); - virtual bool matches(shared_ptr craftSlots, Level *level); + virtual bool matches(shared_ptr craftSlots, Level* level); private: bool matches(shared_ptr craftSlots, int xOffs, int yOffs, bool xFlip); @@ -23,10 +24,14 @@ private: public: virtual shared_ptr assemble(shared_ptr craftSlots); virtual int size(); - ShapedRecipy *keepTag(); + ShapedRecipy* keepTag(); // 4J-PB - to return the items required to make a recipe virtual bool reqs(int iRecipe); - virtual void reqs(INGREDIENTS_REQUIRED *pIngReq); + virtual void reqs(INGREDIENTS_REQUIRED* pIngReq); + + virtual void writeToStream(DataOutputStream* dos); + static ShapedRecipy* readFromStream(DataInputStream* dis); + }; diff --git a/Minecraft.World/ShapelessRecipy.cpp b/Minecraft.World/ShapelessRecipy.cpp index 8fab1b7e..3bf24ad7 100644 --- a/Minecraft.World/ShapelessRecipy.cpp +++ b/Minecraft.World/ShapelessRecipy.cpp @@ -19,6 +19,19 @@ ShapelessRecipy::ShapelessRecipy(ItemInstance *result, vector *i { } +ShapelessRecipy::~ShapelessRecipy() { + for (int i = 0; i < ingredients->size(); i++) { + delete (*ingredients)[i]; + } + + delete ingredients; + delete result; + + ingredients = nullptr; + result = nullptr; +} + + const int ShapelessRecipy::getGroup() { return group; @@ -173,4 +186,61 @@ void ShapelessRecipy::reqs(INGREDIENTS_REQUIRED *pIngReq) delete [] TempIngReq.iIngValA; delete [] TempIngReq.iIngAuxValA; delete [] TempIngReq.uiGridA; -} \ No newline at end of file +} + +void ShapelessRecipy::writeToStream(DataOutputStream* dos) { + dos->writeByte(1); + dos->writeByte(this->group); + + //write result item, it should always be valid + { + dos->writeShort(this->result->id); + dos->writeByte(this->result->count); + dos->writeShort(this->result->getAuxValue()); + + Packet::writeNbt(this->result->tag, dos); + } + + byte iCount = ingredients->size(); + dos->writeByte(iCount); + + for (int i = 0; i < iCount; i++) { + ItemInstance* item = (*ingredients)[i]; + dos->writeBoolean(item == nullptr); + if (item == nullptr) continue; + + dos->writeShort(item->id); + dos->writeShort(item->getAuxValue()); + Packet::writeNbt(item->tag, dos); + } + +} + +ShapelessRecipy* ShapelessRecipy::readFromStream(DataInputStream* dis) { + unsigned char groupType = dis->readByte(); + + int resultItemID = dis->readShort(); + int resultItemCount = dis->readByte(); + int resultItemAux = dis->readShort(); + + ItemInstance* resultItem = new ItemInstance(resultItemID, resultItemCount, 0); + resultItem->setRawAuxValue(resultItemAux); + resultItem->tag = Packet::readNbt(dis); + + vector* ingredients = new vector(); + int iCount = dis->readByte(); + for (int i = 0; i < iCount; i++) { + if (dis->readBoolean() == true) continue; //item is null or something weird + + int itemID = dis->readShort(); + int itemAux = dis->readShort(); + + ItemInstance* ingredients_item = new ItemInstance(itemID, 1, 0); + ingredients_item->setRawAuxValue(itemAux); + ingredients_item->tag = Packet::readNbt(dis); + + ingredients->push_back(ingredients_item); + } + + return new ShapelessRecipy(resultItem, ingredients, (Recipy::_eGroupType)groupType); +} diff --git a/Minecraft.World/ShapelessRecipy.h b/Minecraft.World/ShapelessRecipy.h index bb1dfcf7..8e2c9e3e 100644 --- a/Minecraft.World/ShapelessRecipy.h +++ b/Minecraft.World/ShapelessRecipy.h @@ -9,6 +9,7 @@ private: public: ShapelessRecipy(ItemInstance *result, vector *ingredients, _eGroupType egroup=Recipy::eGroupType_Decoration); + virtual ~ShapelessRecipy() override; virtual const ItemInstance *getResultItem(); virtual const int getGroup(); @@ -20,4 +21,6 @@ public: virtual bool reqs(int iRecipe); virtual void reqs(INGREDIENTS_REQUIRED *pIngReq); + virtual void writeToStream(DataOutputStream* dos); + static ShapelessRecipy* readFromStream(DataInputStream* dos); }; diff --git a/Minecraft.World/SharedConstants.h b/Minecraft.World/SharedConstants.h index c4db88ae..875fdba1 100644 --- a/Minecraft.World/SharedConstants.h +++ b/Minecraft.World/SharedConstants.h @@ -7,7 +7,7 @@ class SharedConstants public: static void staticCtor(); static const wstring VERSION_STRING; - static const int NETWORK_PROTOCOL_VERSION = 78; + static const int NETWORK_PROTOCOL_VERSION = 79; static const bool INGAME_DEBUG_OUTPUT = false; // NOT texture resolution. How many sub-blocks each block face is made up of. @@ -31,4 +31,4 @@ class SharedConstants static const int TICKS_PER_SECOND = 20; static const int FULLBRIGHT_LIGHTVALUE = 15 << 20 | 15 << 4; -}; \ No newline at end of file +}; diff --git a/Minecraft.World/StructureRecipies.cpp b/Minecraft.World/StructureRecipies.cpp index f4397040..c4f6ac77 100644 --- a/Minecraft.World/StructureRecipies.cpp +++ b/Minecraft.World/StructureRecipies.cpp @@ -76,8 +76,52 @@ void StructureRecipies::addRecipes(Recipes *r) L'#', new ItemInstance(Tile::quartzBlock, 1, QuartzBlockTile::TYPE_DEFAULT), L'S'); - - + + r->addShapedRecipy(new ItemInstance(Tile::stone_Id, 2, StoneTile::DIORITE), // + L"ssctcig", + L"#Q", // + L"Q#", // + + L'#', Tile::cobblestone, L'Q', Item::netherQuartz, + L'S'); + + r->addShapedRecipy(new ItemInstance(Tile::stone_Id, 1, StoneTile::GRANITE), // + L"sczcig", + L"#Q", // + + L'#', new ItemInstance(Tile::stone_Id, 1, StoneTile::DIORITE), L'Q', Item::netherQuartz, + L'S'); + + r->addShapedRecipy(new ItemInstance(Tile::stone_Id, 2, StoneTile::ANDESITE), // + L"sczctg", + L"#-", // + + L'#', new ItemInstance(Tile::stone_Id, 1, StoneTile::DIORITE), L'-', Tile::cobblestone, + L'S'); + + r->addShapedRecipy(new ItemInstance(Tile::stone_Id, 4, StoneTile::POLISHED_DIORITE), // + L"ssczg", + L"##", // + L"##", // + + L'#', new ItemInstance(Tile::stone_Id, 1, StoneTile::DIORITE), + L'S'); + + r->addShapedRecipy(new ItemInstance(Tile::stone_Id, 4, StoneTile::POLISHED_GRANITE), // + L"ssczg", + L"##", // + L"##", // + + L'#', new ItemInstance(Tile::stone_Id, 1, StoneTile::GRANITE), + L'S'); + + r->addShapedRecipy(new ItemInstance(Tile::stone_Id, 4, StoneTile::POLISHED_ANDESITE), // + L"ssczg", + L"##", // + L"##", // + + L'#', new ItemInstance(Tile::stone_Id, 1, StoneTile::ANDESITE), + L'S'); // 4J Stu - Changed the order, as the blocks that go with sandstone cause a 3-icon scroll // that touches the text "Structures" in the title in 720 fullscreen. diff --git a/Minecraft.World/SwellGoal.cpp b/Minecraft.World/SwellGoal.cpp index 0fd3c34a..a77037a4 100644 --- a/Minecraft.World/SwellGoal.cpp +++ b/Minecraft.World/SwellGoal.cpp @@ -32,6 +32,12 @@ void SwellGoal::stop() void SwellGoal::tick() { + if(creeper->isIgnited()) + { + creeper->setSwellDir(1); + return; + } + if (target.lock() == nullptr) { creeper->setSwellDir(-1); diff --git a/Minecraft.World/TallGrass2.cpp b/Minecraft.World/TallGrass2.cpp index 7a94c2d2..d1792d4c 100644 --- a/Minecraft.World/TallGrass2.cpp +++ b/Minecraft.World/TallGrass2.cpp @@ -236,11 +236,12 @@ void TallGrass2::neighborChanged(Level* level, int x, int y, int z, int type) if (!isUpper) { - if (!canSurvive(level, x, y, z)) + int upperTileId = level->getTile(x, y + 1, z); + if (!canSurvive(level, x, y, z) || (upperTileId != id)) { spawnResources(level, x, y, z, data, 0); level->setTileAndData(x, y, z, 0, 0, Tile::UPDATE_CLIENTS); - if (level->getTile(x, y + 1, z) == id) + if (upperTileId == id) level->removeTile(x, y + 1, z); } } @@ -258,11 +259,12 @@ void TallGrass2::tick(Level* level, int x, int y, int z, Random* random) if (!isUpper) { - if (!canSurvive(level, x, y, z)) + int upperTileId = level->getTile(x, y + 1, z); + if (!canSurvive(level, x, y, z) || (upperTileId != id)) { spawnResources(level, x, y, z, data, 0); level->setTileAndData(x, y, z, 0, 0, Tile::UPDATE_CLIENTS); - if (level->getTile(x, y + 1, z) == id) + if (upperTileId == id) level->removeTile(x, y + 1, z); } } @@ -271,7 +273,6 @@ void TallGrass2::tick(Level* level, int x, int y, int z, Random* random) int TallGrass2::getResource(int data, Random* random, int playerBonusLevel) { - return -1; } @@ -287,7 +288,6 @@ bool TallGrass2::isSilkTouchable() shared_ptr TallGrass2::getSilkTouchItemInstance(int data) { - if ((data & UPPER_BIT) != 0) return nullptr; int variant = data & ~UPPER_BIT; return std::make_shared(this, 1, variant); diff --git a/Minecraft.World/TeleportEntityPacket.cpp b/Minecraft.World/TeleportEntityPacket.cpp index ccac5e51..7d7a492f 100644 --- a/Minecraft.World/TeleportEntityPacket.cpp +++ b/Minecraft.World/TeleportEntityPacket.cpp @@ -39,7 +39,7 @@ TeleportEntityPacket::TeleportEntityPacket(int id, int x, int y, int z, byte yRo void TeleportEntityPacket::read(DataInputStream *dis) //throws IOException { - id = dis->readShort(); + id = dis->readInt(); #ifdef _LARGE_WORLDS x = dis->readInt(); y = dis->readInt(); @@ -55,7 +55,7 @@ void TeleportEntityPacket::read(DataInputStream *dis) //throws IOException void TeleportEntityPacket::write(DataOutputStream *dos) //throws IOException { - dos->writeShort(id); + dos->writeInt(id); #ifdef _LARGE_WORLDS dos->writeInt(x); dos->writeInt(y); @@ -76,7 +76,7 @@ void TeleportEntityPacket::handle(PacketListener *listener) int TeleportEntityPacket::getEstimatedSize() { - return 2 + 2 + 2 + 2 + 1 + 1; + return 4 + 2 + 2 + 2 + 1 + 1; } bool TeleportEntityPacket::canBeInvalidated() diff --git a/Minecraft.World/Tile.cpp b/Minecraft.World/Tile.cpp index 8c388e03..f7840344 100644 --- a/Minecraft.World/Tile.cpp +++ b/Minecraft.World/Tile.cpp @@ -372,7 +372,7 @@ void Tile::staticCtor() Tile::tiles = new Tile *[TILE_NUM_COUNT]; memset( tiles, 0, sizeof( Tile *)*TILE_NUM_COUNT ); - Tile::stone = (new StoneTile(1)) ->setDestroyTime(1.5f)->setExplodeable(10)->setSoundType(Tile::SOUND_STONE)->setIconName(L"stone")->setDescriptionId(IDS_TILE_STONE)->setUseDescriptionId(IDS_DESC_STONE); + Tile::stone = (new StoneTile(1))->setBaseItemTypeAndMaterial(Item::eBaseItemType_structblock, Item::eMaterial_stone)->setDestroyTime(1.5f)->setExplodeable(10)->setSoundType(Tile::SOUND_STONE)->setIconName(L"stone")->setDescriptionId(IDS_TILE_STONE)->setUseDescriptionId(IDS_DESC_STONE); Tile::grass = static_cast((new GrassTile(2))->setDestroyTime(0.6f)->setSoundType(Tile::SOUND_GRASS)->setIconName(L"grass")->setDescriptionId(IDS_TILE_GRASS)->setUseDescriptionId(IDS_DESC_GRASS)); Tile::dirt = (new DirtTile(3)) ->setDestroyTime(0.5f)->setSoundType(Tile::SOUND_GRAVEL)->setIconName(L"dirt")->setDescriptionId(IDS_TILE_DIRT)->setUseDescriptionId(IDS_DESC_DIRT); Tile::cobblestone = (new Tile(4, Material::stone)) ->setBaseItemTypeAndMaterial(Item::eBaseItemType_structblock, Item::eMaterial_stone)->setDestroyTime(2.0f)->setExplodeable(10)->setSoundType(Tile::SOUND_STONE)->setIconName(L"cobblestone")->setDescriptionId(IDS_TILE_STONE_BRICK)->setUseDescriptionId(IDS_DESC_STONE_BRICK); diff --git a/Minecraft.World/VillagePieces.cpp b/Minecraft.World/VillagePieces.cpp index 96bc7686..9067506c 100644 --- a/Minecraft.World/VillagePieces.cpp +++ b/Minecraft.World/VillagePieces.cpp @@ -709,7 +709,8 @@ BoundingBox *VillagePieces::StraightRoad::findPieceBox(StartPiece *startPiece, l bool VillagePieces::StraightRoad::postProcess(Level *level, Random *random, BoundingBox *chunkBB) { - int tile = biomeBlock(Tile::gravel_Id, 0); + int roadTile = biomeBlock(Tile::gravel_Id, 0); + int baseTile = biomeBlock(Tile::cobblestone_Id, 0); for (int x = boundingBox->x0; x <= boundingBox->x1; x++) { for (int z = boundingBox->z0; z <= boundingBox->z1; z++) @@ -717,7 +718,8 @@ bool VillagePieces::StraightRoad::postProcess(Level *level, Random *random, Boun if (chunkBB->isInside(x, 64, z)) { int y = level->getTopSolidBlock(x, z) - 1; - level->setTileAndData(x, y, z,tile, 0, Tile::UPDATE_CLIENTS); + level->setTileAndData(x, y, z, roadTile, 0, Tile::UPDATE_CLIENTS); + level->setTileAndData(x, y - 1, z, baseTile, 0, Tile::UPDATE_CLIENTS); } } } diff --git a/Minecraft.World/Villager.cpp b/Minecraft.World/Villager.cpp index c608b065..90ade912 100644 --- a/Minecraft.World/Villager.cpp +++ b/Minecraft.World/Villager.cpp @@ -156,7 +156,7 @@ bool Villager::mobInteract(shared_ptr player) shared_ptr item = player->inventory->getSelected(); bool holdingSpawnEgg = item != nullptr && item->id == Item::spawn_egg_Id; - if (!holdingSpawnEgg && isAlive() && !isTrading() && !isBaby()) + if (!player->isSneaking() && !holdingSpawnEgg && isAlive() && !isTrading() && !isBaby()) { if (!level->isClientSide) { @@ -776,3 +776,19 @@ wstring Villager::getDisplayName() }; return app.GetString(name); } + +void Villager::thunderHit(const LightningBolt *lightningBolt) +{ + if (level->isClientSide) return; + shared_ptr witch = std::make_shared(level); + witch->moveTo(x, y, z, yRot, xRot); + + if (this->hasCustomName()) + witch->setCustomName(this->getCustomName()); + + if (this->isPersistenceRequired()) + witch->setPersistenceRequired(); + + level->addEntity(witch); + remove(); +} diff --git a/Minecraft.World/Villager.h b/Minecraft.World/Villager.h index d8aeb15a..f08a028b 100644 --- a/Minecraft.World/Villager.h +++ b/Minecraft.World/Villager.h @@ -147,4 +147,5 @@ public: virtual shared_ptr getBreedOffspring(shared_ptr target); virtual bool canBeLeashed(); virtual wstring getDisplayName(); + virtual void thunderHit(const LightningBolt *lightningBolt); }; \ No newline at end of file diff --git a/Minecraft.World/net.minecraft.world.level.tile.entity.h b/Minecraft.World/net.minecraft.world.level.tile.entity.h index c3209df9..1d6e7f76 100644 --- a/Minecraft.World/net.minecraft.world.level.tile.entity.h +++ b/Minecraft.World/net.minecraft.world.level.tile.entity.h @@ -20,3 +20,4 @@ #include "TheEndPortalTileEntity.h" #include "SkullTileEntity.h" #include "EnderChestTileEntity.h" +#include "ItemFrame.h" diff --git a/NOTES.md b/NOTES.md index f690ecd1..933ebe47 100644 --- a/NOTES.md +++ b/NOTES.md @@ -1,15 +1,16 @@ -![Banner](https://github.com/pieeebot/neoLegacy/raw/main/.github/banner.png) +# neoLegacy v1.0.4b -# neoLegacy v1.0.1b +### Bug Fixes +- Fixed Podzol bottom face displaying incorrect texture (was using side texture instead of dirt) -- Classic Crafting -- Commands support! - - /give - - /tp - /teleport - - /gamemode - - .... +### Changes +- Cursor icon now changes when hovering over different UI elements +- Added TU31 parity changes which include: + - Creepers can now be ignited with Flint and Steel + - Village gravel roads now have Cobblestone underneath + - Villagers now transform into Witches when struck by lightning -image +roadmap # Download Get the latest build from [LCE Emerald Launcher](https://github.com/LCE-Hub/LCE-Emerald-Launcher/releases) or the upcoming LC Launcher. diff --git a/README.md b/README.md index c6124e69..008a0bee 100644 --- a/README.md +++ b/README.md @@ -21,8 +21,8 @@ Users can download our [Nightly Build](https://github.com/pieeebot/neoLegacy/rel Huge thanks to the following projects: - [Patoke/LCERenewed](https://github.com/Patoke/LCERenewed) - for some of the patches that required deep decompilation -- [itsRevela/LCE-Revelations](https://github.com/itsRevela/LCE-Revelations) - for providing a stable project for neoLegacy to continue with -- [GabsPuNs/MinecraftConsoles](https://github.com/GabsPuNs/MinecraftConsoles) - for providing us with their implemention of the Classic Crafting Feature. +- [itsRevela/LCE-Revelations](https://git.revela.dev/itsRevela/LCE-Revelations) - for providing a stable project for neoLegacy to continue with +- [GabsPuNs/Project-Zenith](https://github.com/GabsPuNs/Project-Zenith-Main) - for providing us with their implemention of the Classic Crafting Feature. # Build & Run diff --git a/build-linux.sh b/build-linux.sh index 1e21504b..8b2d238b 100755 --- a/build-linux.sh +++ b/build-linux.sh @@ -1,9 +1,9 @@ #!/usr/bin/env bash set -euo pipefail VERSION="0.0.0" # man we're using nightly :sob: -SOURCE_DIR="${1:-.}" +SOURCE_DIR="${SOURCE_DIR:-${1:-.}}" BUILD_CI="${BUILD_CI:-0}" -BUILD_TYPE="${2:-Release}" +BUILD_TYPE="${BUILD_TYPE:-${2:-Release}}" XWIN_CACHE="${XWIN_CACHE:-$PWD/.xwin}" INSTALL_DIR="${INSTALL_PREFIX:-$HOME/.local/share/neoLegacy}" RED='\033[0;31m' @@ -84,6 +84,10 @@ do_cmake_configure() { -imsvc $winsdk/sdk/include/um \ -imsvc $winsdk/sdk/include/shared" + if [[ "$BUILD_TYPE" == "Debug" ]]; then + c_flags="$c_flags -w" + fi + local linker_flags="\ -libpath:$winsdk/crt/lib/x86_64 \ -libpath:$winsdk/sdk/lib/um/x86_64 \ @@ -259,15 +263,22 @@ LAUNCHER chmod +x "$INSTALL_DIR/minecraft-lce-fourkit" } -BUILD_DIR="$SOURCE_DIR/build/windows64-clang" -mkdir -p "$BUILD_DIR" -info "neoLegacy v$VERSION build script" -info "Source: $SOURCE_DIR | Type: $BUILD_TYPE" -echo "" -check_deps -fetch_winsdk -patch_winsdk_symlinks -do_cmake_configure -do_build -do_install +main() { + BUILD_DIR="$SOURCE_DIR/build/windows64-clang" + mkdir -p "$BUILD_DIR" + info "LegacyEvolved LCE v$VERSION build script" + info "Source: $SOURCE_DIR | Type: $BUILD_TYPE" + echo "" + check_deps + fetch_winsdk + patch_winsdk_symlinks + do_cmake_configure + do_build + do_install +} + +# Do not run main when sourced (required for flake.nix) +if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then + main "$@" +fi diff --git a/cmake/GenerateBuildVer.cmake b/cmake/GenerateBuildVer.cmake index 5db7f0d0..aa4e52bd 100644 --- a/cmake/GenerateBuildVer.cmake +++ b/cmake/GenerateBuildVer.cmake @@ -7,7 +7,7 @@ if(NOT OUTPUT_FILE) message(FATAL_ERROR "OUTPUT_FILE must be set.") endif() -set(BUILD_NUMBER 560) # Note: Build/network has to stay static for now, as without it builds wont be able to play together. We can change it later when we have a better versioning scheme in place. +set(BUILD_NUMBER 570) # Note: Build/network has to stay static for now, as without it builds wont be able to play together. We can change it later when we have a better versioning scheme in place. set(SUFFIX "") # Get short SHA @@ -88,4 +88,4 @@ if(_changed) file(RENAME "${_tmp}" "${OUTPUT_FILE}") else() file(REMOVE "${_tmp}") -endif() \ No newline at end of file +endif() diff --git a/cmake/GenerateSdk.cmake b/cmake/GenerateSdk.cmake new file mode 100644 index 00000000..e96a810e --- /dev/null +++ b/cmake/GenerateSdk.cmake @@ -0,0 +1,228 @@ +if(NOT INPUT_DIRS) + message(FATAL_ERROR "INPUT_DIRS must be set to a list of directories.") +endif() +if(NOT OUTPUT_FILE) + message(FATAL_ERROR "OUTPUT_FILE must be set.") +endif() + +set(_all_headers "") +foreach(_dir IN LISTS INPUT_DIRS) + if(EXISTS "${_dir}") + file(GLOB_RECURSE _hfiles "${_dir}/*.h") + list(APPEND _all_headers ${_hfiles}) + endif() +endforeach() + +if(NOT _all_headers) + message(FATAL_ERROR "No .h files found in INPUT_DIRS.") +endif() +list(REMOVE_DUPLICATES _all_headers) +list(SORT _all_headers) + +foreach(_h IN LISTS _all_headers) + get_filename_component(_bn "${_h}" NAME) + string(TOLOWER "${_bn}" _k) + set(_idx_${_k} "${_h}") +endforeach() + +foreach(_h IN LISTS _all_headers) + file(STRINGS "${_h}" _ll REGEX "^[ \t]*#[ \t]*include[ \t]+\"") + get_filename_component(_hd "${_h}" DIRECTORY) + + set(_dd "") + foreach(_l IN LISTS _ll) + if(_l MATCHES "^[ \t]*#[ \t]*include[ \t]+\"([^\"]+)\"") + set(_in "${CMAKE_MATCH_1}") + set(_rv "") + set(_try "${_hd}/${_in}") + get_filename_component(_try "${_try}" ABSOLUTE) + if(EXISTS "${_try}") + set(_rv "${_try}") + else() + string(TOLOWER "${_in}" _k2) + if(DEFINED _idx_${_k2}) + set(_rv "${_idx_${_k2}}") + endif() + endif() + if(_rv AND _rv IN_LIST _all_headers) + list(APPEND _dd "${_rv}") + endif() + endif() + endforeach() + if(_dd) + list(REMOVE_DUPLICATES _dd) + endif() + set(_de_${_h} "${_dd}") +endforeach() + +set(_sorted "") +set(_left ${_all_headers}) +while(_left) + set(_prog 0) + set(_next "") + foreach(_h IN LISTS _left) + set(_rdy 1) + foreach(_d IN LISTS _de_${_h}) + if(_d IN_LIST _left) + set(_rdy 0) + break() + endif() + endforeach() + if(_rdy) + list(APPEND _sorted "${_h}") + set(_prog 1) + else() + list(APPEND _next "${_h}") + endif() + endforeach() + if(NOT _prog) + foreach(_h IN LISTS _next) + list(APPEND _sorted "${_h}") + endforeach() + break() + endif() + set(_left ${_next}) +endwhile() + +set(_body "") + +foreach(_h IN LISTS _sorted) + file(STRINGS "${_h}" _ll) + + set(_inbc 0) + set(_out "") + foreach(_l IN LISTS _ll) + if(_l STREQUAL "") + continue() + endif() + + if(_inbc) + string(FIND "${_l}" "*/" _ce) + if(_ce GREATER -1) + math(EXPR _ca "${_ce} + 2") + string(SUBSTRING "${_l}" ${_ca} -1 _l) + set(_inbc 0) + else() + continue() + endif() + endif() + + string(FIND "${_l}" "//" _sl) + if(_sl GREATER -1) + string(SUBSTRING "${_l}" 0 ${_sl} _l) + endif() + + while(TRUE) + string(FIND "${_l}" "/*" _so) + if(_so EQUAL -1) + break() + endif() + string(SUBSTRING "${_l}" 0 ${_so} _bf) + string(SUBSTRING "${_l}" ${_so} -1 _ar) + string(FIND "${_ar}" "*/" _ce) + if(_ce EQUAL -1) + set(_l "${_bf}") + set(_inbc 1) + break() + else() + math(EXPR _ca "${_ce} + 2") + string(SUBSTRING "${_ar}" ${_ca} -1 _af) + set(_l "${_bf}${_af}") + endif() + endwhile() + + string(STRIP "${_l}" _ls) + + if(_ls MATCHES "^#[ \t]*pragma[ \t]+once") + continue() + endif() + if(_ls MATCHES "^#[ \t]*include[ \t]+\"([^\"]+)\"") + continue() + endif() + if(_ls MATCHES "^#[ \t]*include[ \t]+<") + continue() + endif() + + if(_ls STREQUAL "") + continue() + endif() + + string(APPEND _out "${_l}\n") + endforeach() + + if(_out) + if(_body) + string(APPEND _body "\n") + endif() + string(APPEND _body "${_out}") + endif() +endforeach() + +set(_guard "MINECRAFT_LCE_SDK_H") +set(_tmp "${OUTPUT_FILE}.tmp") + +file(WRITE "${_tmp}" + "#ifndef ${_guard}\n" + "#define ${_guard}\n" + "\n" + "// Auto-generated. Do not edit.\n" + "// Minecraft Console Edition SDK Header\n" + "\n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#if defined(_WIN32) || defined(_WIN64)\n" + "#include \n" + "#endif\n" + "\n" +) + +file(APPEND "${_tmp}" "${_body}") +file(APPEND "${_tmp}" "\n#endif // ${_guard}\n") + +if(EXISTS "${OUTPUT_FILE}") + execute_process( + COMMAND ${CMAKE_COMMAND} -E compare_files "${OUTPUT_FILE}" "${_tmp}" + RESULT_VARIABLE _ch + ) +else() + set(_ch 1) +endif() + +if(_ch) + file(RENAME "${_tmp}" "${OUTPUT_FILE}") + message(STATUS "GenerateSdk: wrote ${OUTPUT_FILE}") +else() + file(REMOVE "${_tmp}") + message(STATUS "GenerateSdk: ${OUTPUT_FILE} is up-to-date") +endif() diff --git a/flake.lock b/flake.lock index f86a22a8..543f86e9 100644 --- a/flake.lock +++ b/flake.lock @@ -18,6 +18,22 @@ "type": "github" } }, + "fourjlibs": { + "flake": false, + "locked": { + "lastModified": 1777358628, + "narHash": "sha256-yV/ugauN5L4FFC88N4CJI01iTOKYBeim8UQ3Usy2vI0=", + "owner": "Patoke", + "repo": "4JLibs", + "rev": "8fb036f6d6ca5aa5aa2e20633638d6232a58d508", + "type": "github" + }, + "original": { + "owner": "Patoke", + "repo": "4JLibs", + "type": "github" + } + }, "nixpkgs": { "locked": { "lastModified": 1774106199, @@ -37,6 +53,7 @@ "root": { "inputs": { "flake-utils": "flake-utils", + "fourjlibs": "fourjlibs", "nixpkgs": "nixpkgs" } }, diff --git a/flake.nix b/flake.nix index 0dfdb3da..e9589ceb 100644 --- a/flake.nix +++ b/flake.nix @@ -4,18 +4,34 @@ inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; flake-utils.url = "github:numtide/flake-utils"; + fourjlibs = { + url = "github:Patoke/4JLibs"; + flake = false; + }; }; - outputs = { self, nixpkgs, flake-utils }: - flake-utils.lib.eachDefaultSystem (system: + outputs = + { + self, + nixpkgs, + flake-utils, + fourjlibs, + }: + flake-utils.lib.eachDefaultSystem ( + system: let pkgs = import nixpkgs { inherit system; config.allowUnfree = true; }; - # Version info - version = "1.6.0560.0"; + # Version derived from network protocol version in cmake/GenerateBuildVer.cmake + buildNumber = builtins.head ( + builtins.match ".*set\\(BUILD_NUMBER ([0-9]+)\\).*" ( + builtins.readFile ./cmake/GenerateBuildVer.cmake + ) + ); + version = "0.${buildNumber}.0"; # Windows SDK downloaded via xwin (fixed-output derivation) windowsSdk = pkgs.stdenvNoCC.mkDerivation { @@ -24,9 +40,13 @@ outputHashAlgo = "sha256"; outputHashMode = "recursive"; - outputHash = "sha256-ksSytBUjv/tD3IJzHM9BkAzFjJ+JAGD353Pur0G4rQE="; + outputHash = "sha256-UFQjsFVBwcF/9e9tVFoG0Z1JySxyTnFqoaRwr/tUWzA="; - nativeBuildInputs = [ pkgs.xwin pkgs.cacert pkgs.rsync ]; + nativeBuildInputs = [ + pkgs.xwin + pkgs.cacert + pkgs.rsync + ]; dontUnpack = true; @@ -50,8 +70,45 @@ dontFixup = true; }; - # Helper to create case-insensitive symlinks for SDK headers/libs - sdkWithSymlinks = pkgs.runCommand "windows-sdk-symlinked" {} '' + # Pre fetch NuGet packages for FourKit (dotnet publish --self-contained needs win-x64 runtime) + fourkitNugetDeps = pkgs.stdenvNoCC.mkDerivation { + pname = "fourkit-nuget-deps"; + version = "10.0"; + + outputHashAlgo = "sha256"; + outputHashMode = "recursive"; + outputHash = "sha256-eEkU0MugnFSNvVYvd5V5xLK4oNcLgZcXxMYSuiYMPbA="; + + nativeBuildInputs = [ pkgs.cacert ]; + + dontUnpack = true; + + buildPhase = '' + export HOME=$(mktemp -d) + export DOTNET_CLI_TELEMETRY_OPTOUT=1 + export DOTNET_NOLOGO=1 + export SSL_CERT_FILE="${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt" + + # Use the unwrapped SDK to allow NuGet network access + DOTNET="${pkgs.dotnetCorePackages.sdk_10_0.passthru.unwrapped}/share/dotnet/dotnet" + + # Copy csproj to writable location (dotnet needs to write obj/) + WORK=$(mktemp -d) + cp ${./.}/Minecraft.Server.FourKit/Minecraft.Server.FourKit.csproj "$WORK/" + cp ${./.}/global.json "$WORK/" + + $DOTNET restore "$WORK/Minecraft.Server.FourKit.csproj" \ + --runtime win-x64 \ + --packages "$out" \ + --source https://api.nuget.org/v3/index.json + ''; + + dontInstall = true; + dontFixup = true; + }; + + # Helper to make case insensitive symlinks for SDK headers/libs + sdkWithSymlinks = pkgs.runCommand "windows-sdk-symlinked" { } '' cp -r ${windowsSdk} $out chmod -R u+w $out @@ -63,27 +120,6 @@ ln -sf $out/sdk/lib/um/x86_64/ws2_32.lib $out/sdk/lib/um/x86_64/Ws2_32.lib 2>/dev/null || true ''; - # CMake toolchain file for clang-cl cross-compilation - clangClToolchain = pkgs.writeText "clang-cl-toolchain.cmake" '' - set(CMAKE_SYSTEM_NAME Windows) - set(CMAKE_SYSTEM_PROCESSOR AMD64) - - set(CMAKE_C_COMPILER clang-cl) - set(CMAKE_CXX_COMPILER clang-cl) - set(CMAKE_RC_COMPILER llvm-rc) - set(CMAKE_ASM_MASM_COMPILER llvm-ml) - set(CMAKE_AR llvm-lib) - set(CMAKE_LINKER lld-link) - - set(CMAKE_CROSSCOMPILING TRUE) - - set(CMAKE_C_LINK_EXECUTABLE " -out: ") - set(CMAKE_CXX_LINK_EXECUTABLE " -out: ") - - add_compile_options(-fms-compatibility -fms-extensions) - add_compile_definitions(_WIN64 _AMD64_ WIN32_LEAN_AND_MEAN) - ''; - # The main build derivation minecraft-lce-unwrapped = pkgs.stdenv.mkDerivation { pname = "minecraft-lce-unwrapped"; @@ -91,84 +127,79 @@ src = pkgs.lib.cleanSourceWith { src = ./.; - filter = path: type: + filter = + path: type: let baseName = baseNameOf path; in # Exclude build directories and other non-source files - !(baseName == "build" || - baseName == "result" || - baseName == ".git" || - baseName == ".direnv" || - pkgs.lib.hasPrefix "result-" baseName); + !( + baseName == "build" + || baseName == "result" + || baseName == ".git" + || baseName == ".direnv" + || pkgs.lib.hasPrefix "result-" baseName + ); }; + # Patch in the 4JLibs submodule (flakes don't fetch submodules) + postUnpack = '' + rm -rf source/Minecraft.Client/Windows64/4JLibs + cp -r ${fourjlibs} source/Minecraft.Client/Windows64/4JLibs + chmod -R u+w source/Minecraft.Client/Windows64/4JLibs + ''; + nativeBuildInputs = with pkgs; [ - llvmPackages.clang-unwrapped # provides clang-cl - llvmPackages.lld # provides lld-link - llvmPackages.llvm # provides llvm-rc, llvm-ml, llvm-lib, llvm-mt + llvmPackages.clang-unwrapped # provides clang-cl + llvmPackages.lld # provides lld-link + llvmPackages.llvm # provides llvm-rc, llvm-ml, llvm-lib, llvm-mt cmake ninja rsync + winePackage # needed to run fxc.exe during build + dotnetCorePackages.sdk_10_0 # needed for FourKit server ]; - # Set up environment for clang-cl - WINSDK = sdkWithSymlinks; - configurePhase = '' runHook preConfigure - export INCLUDE="$WINSDK/crt/include;$WINSDK/sdk/include/um;$WINSDK/sdk/include/ucrt;$WINSDK/sdk/include/shared" - export LIB="$WINSDK/crt/lib/x86_64;$WINSDK/sdk/lib/um/x86_64;$WINSDK/sdk/lib/ucrt/x86_64" + # Point build-linux.sh at the pre-downloaded Windows SDK + export XWIN_CACHE=$(mktemp -d) + ln -s ${sdkWithSymlinks} "$XWIN_CACHE/splat" - cmake -S . -B build \ - -G Ninja \ - -DCMAKE_BUILD_TYPE=Release \ - -DCMAKE_TOOLCHAIN_FILE=${clangClToolchain} \ - -DCMAKE_C_COMPILER=clang-cl \ - -DCMAKE_CXX_COMPILER=clang-cl \ - -DCMAKE_LINKER=lld-link \ - -DCMAKE_RC_COMPILER=llvm-rc \ - -DCMAKE_MT=llvm-mt \ - -DPLATFORM_DEFINES="_WINDOWS64" \ - -DPLATFORM_NAME="Windows64" \ - -DIGGY_LIBS="iggy_w64.lib;iggyperfmon_w64.lib;iggyexpruntime_w64.lib" \ - -DCMAKE_SYSTEM_NAME=Windows \ - -DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded \ - -DCMAKE_C_FLAGS="/MT -fms-compatibility -fms-extensions --target=x86_64-pc-windows-msvc -imsvc $WINSDK/crt/include -imsvc $WINSDK/sdk/include/ucrt -imsvc $WINSDK/sdk/include/um -imsvc $WINSDK/sdk/include/shared" \ - -DCMAKE_CXX_FLAGS="/MT -fms-compatibility -fms-extensions --target=x86_64-pc-windows-msvc -imsvc $WINSDK/crt/include -imsvc $WINSDK/sdk/include/ucrt -imsvc $WINSDK/sdk/include/um -imsvc $WINSDK/sdk/include/shared" \ - -DCMAKE_ASM_MASM_FLAGS="-m64" \ - -DCMAKE_EXE_LINKER_FLAGS="-libpath:$WINSDK/crt/lib/x86_64 -libpath:$WINSDK/sdk/lib/um/x86_64 -libpath:$WINSDK/sdk/lib/ucrt/x86_64" + # NuGet packages for FourKit dotnet publish + export NUGET_PACKAGES="${fourkitNugetDeps}" + export DOTNET_CLI_TELEMETRY_OPTOUT=1 + export DOTNET_NOLOGO=1 + + # Configure build-linux.sh variables + export SOURCE_DIR=. + export BUILD_TYPE=Release + export INSTALL_PREFIX=$out + + # Source the build script for its functions + source ./build-linux.sh + + BUILD_DIR="$SOURCE_DIR/build/windows64-clang" + mkdir -p "$BUILD_DIR" + + do_cmake_configure + + # Patch shebangs in generated scripts (fxc wine wrapper) + patchShebangs "$BUILD_DIR/tools" 2>/dev/null || true runHook postConfigure ''; buildPhase = '' runHook preBuild - cmake --build build --config Release -j $NIX_BUILD_CORES + do_build runHook postBuild ''; installPhase = '' runHook preInstall - - mkdir -p $out/{client,server} - - # Install client - cp build/Minecraft.Client/Minecraft.Client.exe $out/client/ - cp build/Minecraft.Client/iggy_w64.dll $out/client/ 2>/dev/null || true - cp -r build/Minecraft.Client/Common $out/client/ 2>/dev/null || true - cp -r build/Minecraft.Client/music $out/client/ 2>/dev/null || true - cp -r build/Minecraft.Client/Windows64 $out/client/ 2>/dev/null || true - cp -r build/Minecraft.Client/Windows64Media $out/client/ 2>/dev/null || true - cp -r build/Minecraft.Client/iggy* $out/client/ 2>/dev/null || true - - # Install server - cp build/Minecraft.Server/Minecraft.Server.exe $out/server/ - cp build/Minecraft.Server/iggy_w64.dll $out/server/ 2>/dev/null || true - cp -r build/Minecraft.Server/Common $out/server/ 2>/dev/null || true - cp -r build/Minecraft.Server/Windows64 $out/server/ 2>/dev/null || true - + do_install runHook postInstall ''; @@ -209,40 +240,40 @@ cat > $out/bin/minecraft-lce-client << 'WRAPPER' #!/usr/bin/env bash set -euo pipefail - + GAME_DIR="@gameDir@" PERSIST_DIR="''${MC_DATA_DIR:-$HOME/.local/share/minecraft-lce-client}" - + export WINEARCH=win64 export WINEPREFIX="''${WINEPREFIX:-$HOME/@winePrefixBase@-client}" - + # Wine performance settings export WINEDLLOVERRIDES="winemenubuilder.exe=d" export WINEESYNC=1 export WINEFSYNC=1 export DXVK_LOG_LEVEL=none - + mkdir -p "$PERSIST_DIR" mkdir -p "$WINEPREFIX" - + # Create working directory with symlinks to immutable store WORK_DIR="$(mktemp -d)" trap 'rm -rf "$WORK_DIR"' EXIT - + cp -rs "$GAME_DIR"/* "$WORK_DIR/" chmod -R u+w "$WORK_DIR" - + # Setup persistent data directory mkdir -p "$PERSIST_DIR/GameHDD" rm -rf "$WORK_DIR/Windows64/GameHDD" 2>/dev/null || true ln -sf "$PERSIST_DIR/GameHDD" "$WORK_DIR/Windows64/GameHDD" - + cd "$WORK_DIR" - + echo "[info] Starting Minecraft LCE client" echo "[info] Data directory: $PERSIST_DIR" echo "[info] Wine prefix: $WINEPREFIX" - + exec wine "$WORK_DIR/Minecraft.Client.exe" "$@" WRAPPER @@ -291,33 +322,33 @@ cat > $out/bin/minecraft-lce-server << 'WRAPPER' #!/usr/bin/env bash set -euo pipefail - + GAME_DIR="@gameDir@" SERVER_PORT="''${MC_PORT:-25565}" SERVER_BIND_IP="''${MC_BIND:-0.0.0.0}" PERSIST_DIR="''${MC_DATA_DIR:-$HOME/.local/share/minecraft-lce-server}" - + export WINEARCH=win64 export WINEPREFIX="''${WINEPREFIX:-$HOME/@winePrefixBase@-server}" - + # Wine settings export WINEDLLOVERRIDES="winemenubuilder.exe=d" export WINEESYNC=1 export WINEFSYNC=1 - + mkdir -p "$PERSIST_DIR" mkdir -p "$WINEPREFIX" - + # Create working directory with symlinks to immutable store WORK_DIR="$(mktemp -d)" trap 'rm -rf "$WORK_DIR"' EXIT - + cp -rs "$GAME_DIR"/* "$WORK_DIR/" chmod -R u+w "$WORK_DIR" - + # Setup persistent data mkdir -p "$PERSIST_DIR/GameHDD" - + for file in server.properties banned-players.json banned-ips.json; do if [[ ! -f "$PERSIST_DIR/$file" ]]; then if [[ -f "$WORK_DIR/$file" ]]; then @@ -328,12 +359,12 @@ fi ln -sf "$PERSIST_DIR/$file" "$WORK_DIR/$file" done - + rm -rf "$WORK_DIR/Windows64/GameHDD" 2>/dev/null || true ln -sf "$PERSIST_DIR/GameHDD" "$WORK_DIR/Windows64/GameHDD" - + cd "$WORK_DIR" - + # Start Xvfb if no display (server may require a virtual display) if [[ -z "''${DISPLAY:-}" ]]; then export DISPLAY=":99" @@ -343,11 +374,11 @@ sleep 1 echo "[info] Started Xvfb on $DISPLAY" fi - + echo "[info] Starting Minecraft LCE server on $SERVER_BIND_IP:$SERVER_PORT" echo "[info] Data directory: $PERSIST_DIR" echo "[info] Wine prefix: $WINEPREFIX" - + exec wine "$WORK_DIR/Minecraft.Server.exe" -port "$SERVER_PORT" -bind "$SERVER_BIND_IP" "$@" WRAPPER @@ -386,58 +417,10 @@ rsync coreutils cacert + winePackage ]; text = '' - set -euo pipefail - - SOURCE_DIR="''${1:-.}" - BUILD_TYPE="''${2:-Release}" - XWIN_CACHE="''${XWIN_CACHE:-$HOME/.cache/xwin}" - - export XWIN_CACHE - - echo "[info] Checking Windows SDK cache at $XWIN_CACHE" - - if [[ ! -d "$XWIN_CACHE/splat" ]]; then - echo "[info] Downloading Windows SDK and CRT via xwin..." - mkdir -p "$XWIN_CACHE" - xwin --accept-license splat --output "$XWIN_CACHE/splat" - else - echo "[info] Using cached Windows SDK" - fi - - WINSDK="$XWIN_CACHE/splat" - - export INCLUDE="$WINSDK/crt/include;$WINSDK/sdk/include/um;$WINSDK/sdk/include/ucrt;$WINSDK/sdk/include/shared" - export LIB="$WINSDK/crt/lib/x86_64;$WINSDK/sdk/lib/um/x86_64;$WINSDK/sdk/lib/ucrt/x86_64" - - BUILD_DIR="$SOURCE_DIR/build/windows64-clang" - mkdir -p "$BUILD_DIR" - - echo "[info] Configuring with CMake..." - cmake -S "$SOURCE_DIR" -B "$BUILD_DIR" \ - -G Ninja \ - -DCMAKE_BUILD_TYPE="$BUILD_TYPE" \ - -DCMAKE_TOOLCHAIN_FILE="${clangClToolchain}" \ - -DCMAKE_C_COMPILER=clang-cl \ - -DCMAKE_CXX_COMPILER=clang-cl \ - -DCMAKE_LINKER=lld-link \ - -DCMAKE_RC_COMPILER=llvm-rc \ - -DCMAKE_MT=llvm-mt \ - -DPLATFORM_DEFINES="_WINDOWS64" \ - -DPLATFORM_NAME="Windows64" \ - -DIGGY_LIBS="iggy_w64.lib;iggyperfmon_w64.lib;iggyexpruntime_w64.lib" \ - -DCMAKE_SYSTEM_NAME=Windows \ - -DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded \ - -DCMAKE_C_FLAGS="/MT -fms-compatibility -fms-extensions --target=x86_64-pc-windows-msvc -imsvc $WINSDK/crt/include -imsvc $WINSDK/sdk/include/ucrt -imsvc $WINSDK/sdk/include/um -imsvc $WINSDK/sdk/include/shared" \ - -DCMAKE_CXX_FLAGS="/MT -fms-compatibility -fms-extensions --target=x86_64-pc-windows-msvc -imsvc $WINSDK/crt/include -imsvc $WINSDK/sdk/include/ucrt -imsvc $WINSDK/sdk/include/um -imsvc $WINSDK/sdk/include/shared" \ - -DCMAKE_ASM_MASM_FLAGS="-m64" \ - -DCMAKE_EXE_LINKER_FLAGS="-libpath:$WINSDK/crt/lib/x86_64 -libpath:$WINSDK/sdk/lib/um/x86_64 -libpath:$WINSDK/sdk/lib/ucrt/x86_64" - - echo "[info] Building..." - cmake --build "$BUILD_DIR" --config "$BUILD_TYPE" -j "$(nproc)" - - echo "[info] Build complete! Output in $BUILD_DIR" + exec bash "${./build-linux.sh}" "$@" ''; }; @@ -451,6 +434,9 @@ # Unwrapped (just the Windows executables) unwrapped = minecraft-lce-unwrapped; + # NuGet deps for FourKit (for debugging) + fourkit-nuget-deps = fourkitNugetDeps; + # Windows SDK (for debugging) windows-sdk = sdkWithSymlinks; @@ -488,6 +474,9 @@ xwin rsync + # .NET SDK for FourKit server + dotnetCorePackages.sdk_10_0 + # Wine for testing winePackage winetricks @@ -500,8 +489,6 @@ cacert ]; - XWIN_CACHE = "$HOME/.cache/xwin"; - shellHook = '' echo "LCE-Revelations development shell" echo "" @@ -510,13 +497,14 @@ echo " nix build .#server # Build server package" echo "" echo "Development build (in-tree):" - echo " minecraft-lce-build [source_dir] [Release|Debug]" + echo " ./build-linux.sh [source_dir] [Release|Debug]" echo "" echo "Run:" echo " nix run .#client" echo " nix run .#server" echo "" echo "Environment variables:" + echo " XWIN_CACHE - Windows SDK cache (default: \$PWD/.xwin)" echo " MC_PORT - Server port (default: 25565)" echo " MC_BIND - Server bind address (default: 0.0.0.0)" echo " MC_DATA_DIR - Persistent data directory"