diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 85a0666a..12d5fe38 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -63,6 +63,7 @@ body: id: itsrevela attributes: label: Is this reproducable in itsRevela/LCE-Revelations? (https://github.com/itsRevela/LCE-Revelations) + description: If this was a "no idea" or similar, it will be rejected. validations: required: true @@ -76,6 +77,39 @@ body: validations: required: true + - type: checkboxes + id: screenres + attributes: + label: Screen Resolution(s) + options: + - label: 1080p + - label: 720p + - label: 480p + validations: + required: true + + - type: checkboxes + id: operatingsys + attributes: + label: Operating System(s) + options: + - label: Windows 11 + - label: Windows 10 + - label: Windows 8.1 (not very supported) + - label: Debian(-based) + - label: Arch(-based) + - label: Gentoo(-based) + validations: + required: true + + - type: input + id: launcher + attributes: + label: Launcher + placeholder: e.g. Emerald Launcher + validations: + required: true + - type: textarea id: additional attributes: diff --git a/.github/roadmap.png b/.github/roadmap.png index 38aa48cb..393ed367 100644 Binary files a/.github/roadmap.png and b/.github/roadmap.png differ diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index f7ba5333..a887724c 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -255,7 +255,7 @@ jobs: - \`neoLegacyServerWindows64-FourKit.zip\`: server with the FourKit plugin host, bundled .NET 10 runtime, and an empty \`plugins/\` folder ready for plugin authors to drop DLLs into. Pick the flavour you want and extract it to a folder where you'd like to keep the server runtime." \ - --latest=false + --latest=false --prerelease=true release-client: name: Release Client @@ -333,7 +333,7 @@ jobs: run: | gh release create Nightly artifacts/* \ --title "Client: ${{ steps.sha.outputs.short }}" \ - --notes-file notes.md + --notes-file notes.md --prerelease=true cleanup: needs: [release-client, release-server] diff --git a/.github/workflows/stable.yml b/.github/workflows/stable.yml index 012ff867..cea6c9cc 100644 --- a/.github/workflows/stable.yml +++ b/.github/workflows/stable.yml @@ -2,8 +2,9 @@ name: Stable Release on: push: - branches: - - main + paths: + - 'BUMP' #neo: this is a file. edit it. contains version number + #neo: DO NOT ADD NOTES.md HERE. workflow_dispatch: permissions: @@ -221,33 +222,21 @@ jobs: artifacts/neoLegacyServerWindows64.zip artifacts/neoLegacyServerWindows64-FourKit.zip - - name: Get short SHA - id: sha - run: echo "short=$(echo '${{ github.sha }}' | cut -c1-7)" >> "$GITHUB_OUTPUT" - - - name: Delete old release - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: gh release delete Stable-Dedicated-Server --yes || true - - - name: Delete old tag - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: gh api repos/${{ github.repository }}/git/refs/tags/Stable-Dedicated-Server --method DELETE || true - - name: Create tag run: | + VERSION=v$(cat BUMP) git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" - git tag -f Stable-Dedicated-Server -m "Stable server release ${{ steps.sha.outputs.short }}" - git push origin Stable-Dedicated-Server --force + git tag -f $VERSION-Dedicated-Server -m "$VERSION server" + git push origin $VERSION-Dedicated-Server --force - name: Create release env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | - gh release create Stable-Dedicated-Server artifacts/* \ - --title "Server: ${{ steps.sha.outputs.short }}" \ + VERSION=v$(cat BUMP) + gh release create $VERSION-Dedicated-Server artifacts/* \ + --title "$VERSION Server" \ --notes "Dedicated Server runtime for Windows64. Two flavours are attached: @@ -278,62 +267,22 @@ jobs: subject-path: | artifacts/neoLegacyWindows64.zip - - name: Get short SHA - id: sha - run: echo "short=$(echo '${{ github.sha }}' | cut -c1-7)" >> "$GITHUB_OUTPUT" - - - name: Delete old release - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: gh release delete Stable --yes || true - - - name: Delete old tag - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: gh api repos/${{ github.repository }}/git/refs/tags/Stable --method DELETE || true - - name: Create tag run: | + VERSION=v$(cat BUMP) git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" - git tag -f Stable -m "Stable release ${{ steps.sha.outputs.short }}" - git push origin Stable --force - - - name: Write release notes - run: | - cat > notes.md <<'NOTES' - # Instructions: - **Newcomers:** - - If this is your first time, download `neoLegacyWindows64.zip` and extract it wherever you would like to keep it. - - I would recommend to set your username prior to launch (create a file called `username.txt`, put your desired username into the file, and save). - - To play, simply run `Minecraft.Client.exe`. - - **Steam Deck & Linux:** - - Y'all know the drill. Download the `neoLegacyWindows64.zip`, extract it, add the `Minecraft.Client.exe` as a "Non-Steam Game" within the Steam library, turn on compatibility mode with Proton Experimental, and then run it! - - # Multiplayer instructions: - LAN games are natively supported, and any LAN games will appear automatically on the right. However, if you'd like to play with your friends online (and if you don't want to require them to setup a vpn, and/or if you don't want to port forward), I would recommend the following setup. Please keep in mind, you do NOT need to do this to enjoy the game. This is just how I have it setup for me so my friends can join without any hassle: - - Prerequisites: - - Premium playit.gg account, costs about $3 USD per month. This is for setting up the tunnel. - - playit.gg agent installed on host PC. - - How-to: - - Ensure your playit.gg agent is connected to your playit.gg account - - On the playit.gg website, setup a new tunnel (choose TCP). Ensure the configurable settings are set to the below values, assuming your agent is installed on the same computer as your online neoLegacyMinecraft game is hosted from. - - Configurable settings: - - Local IP: `127.0.0.1` - - Local Port: `25565` - - Proxy Protocol: `None` - - After creating your tunnel, navigate to the "Tunnels" main page. You'll see the IP address and port for your tunnel. This is what your friends will input when adding your server in order to join your online game! + git tag -f $VERSION -m "Stable release $VERSION" + git push origin $VERSION --force - name: Create release env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | - gh release create Stable artifacts/* \ - --title "Client: ${{ steps.sha.outputs.short }}" \ - --notes-file notes.md + VERSION=v$(cat BUMP) + gh release create $VERSION artifacts/* \ + --title "$VERSION" \ + --notes-file NOTES.md cleanup: needs: [release-client, release-server] diff --git a/BUMP b/BUMP new file mode 100644 index 00000000..a60b8fce --- /dev/null +++ b/BUMP @@ -0,0 +1 @@ +1.0.1b diff --git a/CMakeLists.txt b/CMakeLists.txt index 3a8257ae..5623ed4a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -234,6 +234,34 @@ if(TARGET Minecraft.Server) add_dependencies(Minecraft.Server GenerateStringIdLookup) endif() +set(_item_map_inputs + "${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.World/Tile.h" + "${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.World/Item.h" +) + +#neo: added ItemNameMap generation +add_custom_command( + OUTPUT "${CMAKE_BINARY_DIR}/generated/ItemNameMap.h" + COMMAND ${CMAKE_COMMAND} + "-DINPUT_FILES=${_item_map_inputs}" + "-DOUTPUT_FILE=${CMAKE_BINARY_DIR}/generated/ItemNameMap.h" + -P "${CMAKE_CURRENT_SOURCE_DIR}/cmake/GenerateItemNameMap.cmake" + DEPENDS + "${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.World/Tile.h" + "${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.World/Item.h" + COMMENT "Generating ItemNameMap.h" +) + +add_custom_target(GenerateItemNameMap ALL + DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/generated/ItemNameMap.h" +) + +add_dependencies(Minecraft.Client GenerateItemNameMap) +add_dependencies(Minecraft.World GenerateItemNameMap) +if(PLATFORM_NAME STREQUAL "Windows64") + add_dependencies(Minecraft.Server GenerateItemNameMap) +endif() + target_include_directories(Minecraft.Client PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/generated" ) diff --git a/Minecraft.Client/Common/Audio/SoundNames.cpp b/Minecraft.Client/Common/Audio/SoundNames.cpp index c82228e2..7c0dd382 100644 --- a/Minecraft.Client/Common/Audio/SoundNames.cpp +++ b/Minecraft.Client/Common/Audio/SoundNames.cpp @@ -272,7 +272,23 @@ const WCHAR *ConsoleSoundEngine::wchSoundNames[eSoundType_MAX]= L"item.armor.equip_generic6", L"damage.critical", //eSoundType_DAMAGE_CRITICAL, - L"item.elytra.flying" // eSoundType_ITEM_ELYTRA_FLYING + L"item.elytra.flying", // eSoundType_ITEM_ELYTRA_FLYING + + L"mob.guardian.attack_loop", + L"mob.guardian.guardian_death", + L"mob.guardian.guardian_hit", + + L"mob.guardian.flop", + + L"mob.guardian.land_death", + L"mob.guardian.land_hit", + L"mob.guardian.land_idle", + + L"mob.guardian.curse", + L"mob.guardian.elder_death", + L"mob.guardian.elder_hit", + L"mob.guardian.elder_idle" + }; diff --git a/Minecraft.Client/Common/Media/MediaWindows64/SettingsUIMenu1080.swf b/Minecraft.Client/Common/Media/MediaWindows64/SettingsUIMenu1080.swf deleted file mode 100644 index 26e58cfa..00000000 Binary files a/Minecraft.Client/Common/Media/MediaWindows64/SettingsUIMenu1080.swf and /dev/null differ diff --git a/Minecraft.Client/Common/Media/MediaWindows64/SettingsUIMenu480.swf b/Minecraft.Client/Common/Media/MediaWindows64/SettingsUIMenu480.swf deleted file mode 100644 index 5ddf457e..00000000 Binary files a/Minecraft.Client/Common/Media/MediaWindows64/SettingsUIMenu480.swf and /dev/null differ diff --git a/Minecraft.Client/Common/Media/MediaWindows64/SettingsUIMenu720.swf b/Minecraft.Client/Common/Media/MediaWindows64/SettingsUIMenu720.swf index 36a7dbec..0ff83a37 100644 Binary files a/Minecraft.Client/Common/Media/MediaWindows64/SettingsUIMenu720.swf and b/Minecraft.Client/Common/Media/MediaWindows64/SettingsUIMenu720.swf differ diff --git a/Minecraft.Client/Common/Media/MediaWindows64/SettingsUIMenuSplit720.swf b/Minecraft.Client/Common/Media/MediaWindows64/SettingsUIMenuSplit720.swf index 5ca09874..82c22145 100644 Binary files a/Minecraft.Client/Common/Media/MediaWindows64/SettingsUIMenuSplit720.swf and b/Minecraft.Client/Common/Media/MediaWindows64/SettingsUIMenuSplit720.swf differ diff --git a/Minecraft.Client/Common/UI/IUIScene_PauseMenu.cpp b/Minecraft.Client/Common/UI/IUIScene_PauseMenu.cpp index fe118919..fa10afc4 100644 --- a/Minecraft.Client/Common/UI/IUIScene_PauseMenu.cpp +++ b/Minecraft.Client/Common/UI/IUIScene_PauseMenu.cpp @@ -438,6 +438,7 @@ static bool Win64_DeleteSaveDirectory(const wchar_t* wPath) // This function performs the meat of exiting from a level. It should be called from a thread other than the main thread. void IUIScene_PauseMenu::_ExitWorld(LPVOID lpParameter) { +#ifndef MINECRAFT_SERVER_BUILD Minecraft *pMinecraft=Minecraft::GetInstance(); // 4J Added: Capture hardcore delete info before the server is destroyed @@ -724,6 +725,7 @@ void IUIScene_PauseMenu::_ExitWorld(LPVOID lpParameter) // Make sure we don't think saving is disabled in the menus StorageManager.SetSaveDisabled(false); #endif +#endif } @@ -780,4 +782,4 @@ int IUIScene_PauseMenu::DisableAutosaveDialogReturned(void *pParam,int iPad,C4JS app.SetAction(iPad,eAppAction_SaveGame); } return 0; -} \ No newline at end of file +} diff --git a/Minecraft.Client/GuardianModel.cpp b/Minecraft.Client/GuardianModel.cpp index 775a76c2..18ef11f9 100644 --- a/Minecraft.Client/GuardianModel.cpp +++ b/Minecraft.Client/GuardianModel.cpp @@ -3,6 +3,7 @@ #include "../Minecraft.World/Mth.h" #include "ModelPart.h" #include "../Minecraft.World/Guardian.h" +#include @@ -70,34 +71,41 @@ void GuardianModel::setupAnim(float time, float r, float bob, float yRot, float float afloat4[] = {-8.0f, -8.0f, -8.0f, -8.0f, 0.0f, 0.0f, 0.0f, 0.0f, 8.0f, 8.0f, 8.0f, 8.0f }; float afloat5[] = { 8.0f, -8.0f, 0.0f, 0.0f,-8.0f,-8.0f, 8.0f, 8.0f, 8.0f, -8.0f, 0.0f, 0.0f }; - // Body orientation + guardianBody->yRot = yRot / (180.0f / (float)PI); guardianBody->xRot = xRot / (180.0f / (float)PI); guardianEye->z = -8.25f; + shared_ptr eyeTarget = nullptr; + if (guardian->hasTargetedEntity()) { - shared_ptr target = guardian->getTargetedEntity(); - if (target) - { - - double d0 = (guardian->y + guardian->getEyeHeight()) - - (target->y + target->getEyeHeight()); - guardianEye->y = (d0 > 0.0) ? 0.0f : 1.0f; + eyeTarget = guardian->getTargetedEntity(); + } - - double dx = target->x - guardian->x; - double dz = target->z - guardian->z; - float targetYaw = (float)(atan2(dz, dx) * 180.0 / PI) - 90.0f; - float diff = Mth::wrapDegrees(targetYaw - guardian->yRot); - float clamped = diff / 45.0f; - if (clamped < -2.0f) clamped = -2.0f; - if (clamped > 2.0f) clamped = 2.0f; - guardianEye->x = clamped; - } + if (eyeTarget == nullptr && EntityRenderDispatcher::instance != nullptr) + { + eyeTarget = EntityRenderDispatcher::instance->cameraEntity; + } + + if (eyeTarget != nullptr) + { + double d0 = (guardian->y + guardian->getEyeHeight()) + - (eyeTarget->y + eyeTarget->getEyeHeight()); + guardianEye->y = (d0 > 0.0) ? 0.0f : 1.0f; + + double dx = eyeTarget->x - guardian->x; + double dz = eyeTarget->z - guardian->z; + float targetYaw = (float)(atan2(dz, dx) * 180.0 / PI) - 90.0f; + float diff = Mth::wrapDegrees(targetYaw - guardian->yRot); + + float clamped = diff / 45.0f; + if (clamped < -2.0f) clamped = -2.0f; + if (clamped > 2.0f) clamped = 2.0f; + guardianEye->x = clamped; } else { @@ -106,6 +114,7 @@ void GuardianModel::setupAnim(float time, float r, float bob, float yRot, float } + float f1 = (1.0f - guardian->getSpikesAnimation(0.0f)) * 0.55f; for (int i = 0; i < 12; ++i) diff --git a/Minecraft.Client/GuardianRenderer.cpp b/Minecraft.Client/GuardianRenderer.cpp index a8f851bf..75a89fc6 100644 --- a/Minecraft.Client/GuardianRenderer.cpp +++ b/Minecraft.Client/GuardianRenderer.cpp @@ -110,8 +110,6 @@ void GuardianRenderer::render(shared_ptr _mob, double x, double y, doubl float time = (float)mob->tickCount + a; float texVOff = -time * 0.2f - floor(-time * 0.1f); float eyeHeight = mob->getEyeHeight(); - if (mob && mob->isElder()) - eyeHeight = mob->bbHeight * 0.5f * 2.35f; double targetX = target->xo + (target->x - target->xo) * a; @@ -138,8 +136,8 @@ void GuardianRenderer::render(shared_ptr _mob, double x, double y, doubl glDisable(GL_LIGHTING); glDisable(GL_CULL_FACE); glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glDepthMask(true); + glBlendFunc(GL_SRC_ALPHA, GL_ONE); + glDepthMask(false); glPushMatrix(); glTranslatef((float)x, (float)y + eyeHeight, (float)z); @@ -214,11 +212,22 @@ void GuardianRenderer::render(shared_ptr _mob, double x, double y, doubl glDepthMask(true); glDisable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_LIGHTING); glEnable(GL_CULL_FACE); glPopMatrix(); } + +void GuardianRenderer::scale(shared_ptr mob, float a) +{ + shared_ptr guardian = dynamic_pointer_cast(mob); + if (guardian && guardian->isElder()) + { + glScalef(2.35f, 2.35f, 2.35f); + } +} + void GuardianRenderer::renderModel(shared_ptr mob, float wp, float ws, float bob, float headRotMinusBodyRot, float headRotx, float scale) { @@ -226,10 +235,11 @@ void GuardianRenderer::renderModel(shared_ptr mob, float wp, float if (guardian && guardian->isElder()) { glPushMatrix(); - - glTranslatef(0.0f, -2.0f, 0.0f); - glScalef(2.35f, 2.35f, 2.35f); + + + LivingEntityRenderer::renderModel(mob, wp, ws, bob, headRotMinusBodyRot, headRotx, scale); + glPopMatrix(); } else diff --git a/Minecraft.Client/GuardianRenderer.h b/Minecraft.Client/GuardianRenderer.h index 3b09d738..cb7fe7c6 100644 --- a/Minecraft.Client/GuardianRenderer.h +++ b/Minecraft.Client/GuardianRenderer.h @@ -20,7 +20,7 @@ private: public: GuardianRenderer(Model *model, float shadow); - + void scale(shared_ptr mob, float a)override; virtual bool shouldRender(shared_ptr mob, float camX, float camY, float camZ) override; diff --git a/Minecraft.Client/LevelRenderer.cpp b/Minecraft.Client/LevelRenderer.cpp index 544662a8..7ea382a2 100644 --- a/Minecraft.Client/LevelRenderer.cpp +++ b/Minecraft.Client/LevelRenderer.cpp @@ -28,6 +28,7 @@ #include "BreakingItemParticle.h" #include "SnowShovelParticle.h" #include "BreakingItemParticle.h" +#include "MobAppearanceParticle.h" #include "HeartParticle.h" #include "HugeExplosionParticle.h" #include "HugeExplosionSeedParticle.h" @@ -140,8 +141,9 @@ LevelRenderer::LevelRenderer(Minecraft *mc, Textures *textures) culledEntities = 0; chunkFixOffs = 0; frame = 0; +#ifndef MINECRAFT_SERVER_BUILD repeatList = MemoryTracker::genLists(1); - +#endif destroyProgress = 0.0f; totalChunks= offscreenChunks= occludedChunks= renderedChunks= emptyChunks = 0; @@ -170,7 +172,7 @@ LevelRenderer::LevelRenderer(Minecraft *mc, Textures *textures) this->mc = mc; this->textures = textures; - +#ifndef MINECRAFT_SERVER_BUILD chunkLists = MemoryTracker::genLists(getGlobalChunkCount() * CHUNK_RENDER_LAYERS); // One render list per chunk render layer. globalChunkFlags = new unsigned char[getGlobalChunkCount()]; memset(globalChunkFlags, 0, getGlobalChunkCount()); @@ -260,6 +262,7 @@ LevelRenderer::LevelRenderer(Minecraft *mc, Textures *textures) t->end(); glEndList(); } +#endif Chunk::levelRenderer = this; @@ -535,6 +538,7 @@ void LevelRenderer::allChanged(int playerIndex) void LevelRenderer::renderEntities(Vec3 *cam, Culler *culler, float a) { +#ifndef MINECRAFT_SERVER_BUILD if (mc == nullptr || mc->player == nullptr) { return; @@ -661,6 +665,7 @@ void LevelRenderer::renderEntities(Vec3 *cam, Culler *culler, float a) LeaveCriticalSection(&m_csRenderableTileEntities); mc->gameRenderer->turnOffLightLayer(a); // 4J - brought forward from 1.8.2 +#endif } wstring LevelRenderer::gatherStats1() @@ -822,6 +827,7 @@ void LevelRenderer::renderChunksDirect(int layer, double alpha) #ifdef __PSVITA__ #include + // this is need to sort the chunks by depth typedef struct { @@ -2859,7 +2865,7 @@ shared_ptr LevelRenderer::addParticleInternal(ePARTICLE_TYPE eParticle // 4J - the java code doesn't distance cull these two particle types, we need to implement this behaviour differently as our distance check is // mixed up with other things bool distCull = true; - if ( (eParticleType == eParticleType_hugeexplosion) || (eParticleType == eParticleType_largeexplode) || (eParticleType == eParticleType_dragonbreath) || (eParticleType == eParticleType_wake)) + if ( (eParticleType == eParticleType_hugeexplosion) || (eParticleType == eParticleType_largeexplode) || (eParticleType == eParticleType_dragonbreath) || (eParticleType == eParticleType_wake)||(eParticleType == eParticleType_mobAppearance)) { distCull = false; } @@ -3075,6 +3081,10 @@ shared_ptr LevelRenderer::addParticleInternal(ePARTICLE_TYPE eParticle case eParticleType_barrier: particle = std::make_shared(lev, x, y, z, xa, ya, za); break; + case eParticleType_mobAppearance: + particle = std::make_shared(lev, x, y, z); + break; + default: if( ( eParticleType >= eParticleType_iconcrack_base ) && ( eParticleType <= eParticleType_iconcrack_last ) ) { @@ -3954,7 +3964,9 @@ int LevelRenderer::rebuildChunkThreadProc(LPVOID lpParam) AABB::CreateNewThreadStorage(); IntCache::CreateNewThreadStorage(); Tesselator::CreateNewThreadStorage(1024*1024); +#ifndef MINECRAFT_SERVER_BUILD RenderManager.InitialiseContext(); +#endif Chunk::CreateNewThreadStorage(); Tile::CreateNewThreadStorage(); diff --git a/Minecraft.Client/Minecraft.cpp b/Minecraft.Client/Minecraft.cpp index 76a177e5..0095e3cc 100644 --- a/Minecraft.Client/Minecraft.cpp +++ b/Minecraft.Client/Minecraft.cpp @@ -141,8 +141,10 @@ Minecraft::Minecraft(Component *mouseComponent, Canvas *parent, MinecraftApplet user = nullptr; parent = nullptr; pause = false; +#ifndef MINECRAFT_SERVER_BUILD textures = nullptr; font = nullptr; +#endif screen = nullptr; localPlayerIdx = 0; rightClickDelay = 0; @@ -151,8 +153,9 @@ Minecraft::Minecraft(Component *mouseComponent, Canvas *parent, MinecraftApplet InitializeCriticalSection( &ProgressRenderer::s_progress ); InitializeCriticalSection(&m_setLevelCS); //m_hPlayerRespawned = CreateEvent(nullptr, FALSE, FALSE, nullptr); - +#ifndef MINECRAFT_SERVER_BUILD progressRenderer = nullptr; +#endif gameRenderer = nullptr; bgLoader = nullptr; @@ -166,8 +169,12 @@ Minecraft::Minecraft(Component *mouseComponent, Canvas *parent, MinecraftApplet orgWidth = orgHeight = 0; achievementPopup = new AchievementPopup(this); gui = nullptr; +#ifndef MINECRAFT_SERVER_BUILD noRender = false; humanoidModel = new HumanoidModel(0); +#else + noRender = true; +#endif hitResult = nullptr; options = nullptr; soundEngine = new SoundEngine(); @@ -338,12 +345,13 @@ void Minecraft::init() options = new Options(this, workingDirectory); skins = new TexturePackRepository(workingDirectory, this); skins->addDebugPacks(); +#ifndef MINECRAFT_SERVER_BUILD textures = new Textures(skins, options); //renderLoadingScreen(); font = new Font(options, L"font/Default.png", textures, false, &DEFAULT_FONT_LOCATION, 23, 20, 8, 8, SFontData::Codepoints); altFont = new Font(options, L"font/alternate.png", textures, false, &ALT_FONT_LOCATION, 16, 16, 8, 8); - +#endif //if (options.languageCode != null) { // Language.getInstance().loadLanguage(options.languageCode); // // font.setEnforceUnicodeSheet("true".equalsIgnoreCase(I18n.get("language.enforceUnicode"))); @@ -357,7 +365,9 @@ void Minecraft::init() //FoliageColor::init(textures->loadTexturePixels(L"misc/foliagecolor.png")); gameRenderer = new GameRenderer(this); +#ifndef MINECRAFT_SERVER_BUILD EntityRenderDispatcher::instance->itemInHandRenderer = new ItemInHandRenderer(this,false); +#endif for( int i=0 ; i<4 ; ++i ) stats[i] = new StatsCounter(); @@ -384,6 +394,7 @@ void Minecraft::init() e.printStackTrace(); } #endif +#ifndef MINECRAFT_SERVER_BUILD MemSect(31); checkGlError(L"Pre startup"); @@ -407,12 +418,17 @@ void Minecraft::init() MemSect(31); checkGlError(L"Startup"); MemSect(0); - +#endif // openGLCapabilities = new OpenGLCapabilities(); // 4J - removed - +#ifndef MINECRAFT_SERVER_BUILD levelRenderer = new LevelRenderer(this, textures); +#else + levelRenderer = new LevelRenderer(this, nullptr); +#endif //textures->register(&TextureAtlas::LOCATION_BLOCKS, new TextureAtlas(Icon::TYPE_TERRAIN, TN_TERRAIN)); //textures->register(&TextureAtlas::LOCATION_ITEMS, new TextureAtlas(Icon::TYPE_ITEM, TN_GUI_ITEMS)); +#ifndef MINECRAFT_SERVER_BUILD + textures->stitch(); glViewport(0, 0, width, height); @@ -424,6 +440,7 @@ void Minecraft::init() MemSect(0); gui = new Gui(this); + if (connectToIp != L"") // 4J - was nullptr comparison { // setScreen(new ConnectScreen(this, connectToIp, connectToPort)); // 4J TODO - put back in @@ -435,6 +452,7 @@ void Minecraft::init() progressRenderer = new ProgressRenderer(this); RenderManager.CBuffLockStaticCreations(); +#endif } void Minecraft::renderLoadingScreen() @@ -1262,11 +1280,14 @@ void Minecraft::run_middle() if(running) { +#ifndef MINECRAFT_SERVER_BUILD if (reloadTextures) { reloadTextures = false; textures->reloadAll(); } +#endif + //while (running) { @@ -2340,13 +2361,18 @@ void Minecraft::tick(bool bFirst, bool bUpdateTextures) // soundEngine.playMusicTick(); if (!pause && level != nullptr) gameMode->tick(); +#ifndef MINECRAFT_SERVER_BUILD MemSect(31); glBindTexture(GL_TEXTURE_2D, textures->loadTexture(TN_TERRAIN)); //L"/terrain.png")); MemSect(0); +#endif + if( bFirst ) { PIXBeginNamedEvent(0,"Texture tick"); +#ifndef MINECRAFT_SERVER_BUILD if (!pause) textures->tick(bUpdateTextures); +#endif PIXEndNamedEvent(); } @@ -4360,12 +4386,13 @@ void Minecraft::setLevel(MultiPlayerLevel *level, int message /*=-1*/, shared_pt EnterCriticalSection(&m_setLevelCS); bool playerAdded = false; this->cameraTargetPlayer = nullptr; - +#ifdef MINECRAFT_SERVER_BUILD if(progressRenderer != nullptr) { this->progressRenderer->progressStart(message); this->progressRenderer->progressStage(-1); } +#endif // Stop menu music and transition to game music for the new level soundEngine->playStreaming(L"", 0, 0, 0, 1, 1); @@ -4588,11 +4615,14 @@ void Minecraft::setLevel(MultiPlayerLevel *level, int message /*=-1*/, shared_pt void Minecraft::prepareLevel(int title) { +#ifndef MINECRAFT_SERVER_BUILD if(progressRenderer != nullptr) { this->progressRenderer->progressStart(title); this->progressRenderer->progressStage(IDS_PROGRESS_BUILDING_TERRAIN); } +#endif + int r = 128; if (gameMode->isCutScene()) r = 64; int pp = 0; @@ -4616,7 +4646,7 @@ void Minecraft::prepareLevel(int title) spcc->centerOn(spawnPos->x >> 4, spawnPos->z >> 4); } #endif - +#ifndef MINECRAFT_SERVER_BUILD for (int x = -r; x <= r; x += 16) { for (int z = -r; z <= r; z += 16) @@ -4632,7 +4662,8 @@ void Minecraft::prepareLevel(int title) { if(progressRenderer != nullptr) this->progressRenderer->progressStage(IDS_PROGRESS_SIMULATING_WORLD); max = 2000; -} + } +#endif } wstring Minecraft::gatherStats1() @@ -4892,8 +4923,10 @@ void Minecraft::main() useLomp = true; MinecraftWorld_RunStaticCtors(); +#ifndef MINECRAFT_SERVER_BUILD EntityRenderDispatcher::staticCtor(); TileEntityRenderDispatcher::staticCtor(); +#endif User::staticCtor(); Tutorial::staticCtor(); ColourTable::staticCtor(); diff --git a/Minecraft.Client/MinecraftServer.cpp b/Minecraft.Client/MinecraftServer.cpp index 3e15b344..db87c954 100644 --- a/Minecraft.Client/MinecraftServer.cpp +++ b/Minecraft.Client/MinecraftServer.cpp @@ -559,8 +559,10 @@ MinecraftServer::MinecraftServer() m_bLoaded = false; stopped = false; tickCount = 0; +#ifndef MINECRAFT_SERVER_BUILD wstring progressStatus; progress = 0; +#endif motd = L""; m_isServerPaused = false; @@ -736,8 +738,10 @@ bool MinecraftServer::initServer(int64_t seed, NetworkGameInitData *initData, DW pLevelType = LevelType::lvl_normal; } +#ifndef MINECRAFT_SERVER_BUILD ProgressRenderer *mcprogress = Minecraft::GetInstance()->progressRenderer; mcprogress->progressStart(IDS_PROGRESS_INITIALISING_SERVER); +#endif if( findSeed ) { @@ -876,7 +880,9 @@ void MinecraftServer::postProcessTerminate(ProgressRenderer *mcprogress) if( postProcessItemCount ) { +#ifndef MINECRAFT_SERVER_BUILD mcprogress->progressStagePercentage((postProcessItemCount - postProcessItemRemaining) * 100 / postProcessItemCount); +#endif } CompressedTileStorage::tick(); SparseLightStorage::tick(); @@ -1030,7 +1036,7 @@ bool MinecraftServer::loadLevel(LevelStorageSource *storageSource, const wstring players->setLevel(levels); } - +#ifndef MINECRAFT_SERVER_BUILD if( levels[0]->isNew ) { mcprogress->progressStage(IDS_PROGRESS_GENERATING_SPAWN_AREA); @@ -1039,6 +1045,7 @@ bool MinecraftServer::loadLevel(LevelStorageSource *storageSource, const wstring { mcprogress->progressStage(IDS_PROGRESS_LOADING_SPAWN_AREA); } +#endif app.SetGameHostOption( eGameHostOption_HasBeenInCreative, gameType == GameType::CREATIVE || levels[0]->getHasBeenInCreative() ); app.SetGameHostOption( eGameHostOption_Structures, levels[0]->isGenerateMapFeatures() ); @@ -1151,7 +1158,12 @@ bool MinecraftServer::loadLevel(LevelStorageSource *storageSource, const wstring { delete spawnPos; m_postUpdateTerminate = true; +#ifndef MINECRAFT_SERVER_BUILD postProcessTerminate(mcprogress); +#else + postProcessTerminate(nullptr); +#endif + return false; } // printf(">>>%d %d %d\n",i,x,z); @@ -1161,7 +1173,9 @@ bool MinecraftServer::loadLevel(LevelStorageSource *storageSource, const wstring { int pos = (x + r) * twoRPlusOne + (z + 1); // setProgress(L"Preparing spawn area", (pos) * 100 / total); +#ifndef MINECRAFT_SERVER_BUILD mcprogress->progressStagePercentage((pos+r) * 100 / total); +#endif // lastTime = now; } static int count = 0; @@ -1203,7 +1217,11 @@ bool MinecraftServer::loadLevel(LevelStorageSource *storageSource, const wstring // Wait for post processing, then lighting threads, to end (post-processing may make more lighting changes) m_postUpdateTerminate = true; +#ifndef MINECRAFT_SERVER_BUILD postProcessTerminate(mcprogress); +#else + postProcessTerminate(nullptr); +#endif // stronghold position? @@ -1245,14 +1263,22 @@ bool MinecraftServer::loadLevel(LevelStorageSource *storageSource, const wstring if( levels[1]->isNew ) { +#ifndef MINECRAFT_SERVER_BUILD levels[1]->save(true, mcprogress); +#else + levels[1]->save(true, nullptr); +#endif } if( s_bServerHalted || !g_NetworkManager.IsInSession() ) return false; if( levels[2]->isNew ) { +#ifndef MINECRAFT_SERVER_BUILD levels[2]->save(true, mcprogress); +#else + levels[2]->save(true, nullptr); +#endif } if( s_bServerHalted || !g_NetworkManager.IsInSession() ) return false; @@ -1264,7 +1290,11 @@ bool MinecraftServer::loadLevel(LevelStorageSource *storageSource, const wstring if( levels[0]->isNew ) { +#ifndef MINECRAFT_SERVER_BUILD levels[0]->save(true, mcprogress); +#else + levels[0]->save(true, nullptr); +#endif } if( s_bServerHalted || !g_NetworkManager.IsInSession() ) return false; @@ -1366,15 +1396,19 @@ void MinecraftServer::overwriteHellBordersForNewWorldSize(ServerLevel* level, in void MinecraftServer::setProgress(const wstring& status, int progress) { +#ifndef MINECRAFT_SERVER_BUILD progressStatus = status; this->progress = progress; +#endif // logger.info(status + ": " + progress + "%"); } void MinecraftServer::endProgress() { +#ifndef MINECRAFT_SERVER_BUILD progressStatus = L""; this->progress = 0; +#endif } void MinecraftServer::saveAllChunks() @@ -1392,8 +1426,11 @@ void MinecraftServer::saveAllChunks() ServerLevel *level = levels[levels.length - 1 - i]; if( level ) // 4J - added check as level can be nullptr if we end up in stopServer really early on due to network failure { +#ifndef MINECRAFT_SERVER_BUILD level->save(true, Minecraft::GetInstance()->progressRenderer); - +#else + level->save(true, nullptr); +#endif // Only close the level storage when we have saved the last level, otherwise we need to recreate the region files // when saving the next levels if( i == (levels.length - 1)) @@ -1524,7 +1561,11 @@ void MinecraftServer::stopServer(bool didInit) { if (players != nullptr) { +#ifndef MINECRAFT_SERVER_BUILD players->saveAll(Minecraft::GetInstance()->progressRenderer, true); +#else + players->saveAll(nullptr, true); +#endif } // 4J Stu - Save the levels in reverse order so we don't overwrite the level.dat // with the data from the nethers leveldata. @@ -1542,7 +1583,11 @@ void MinecraftServer::stopServer(bool didInit) app.m_gameRules.unloadCurrentGameRules(); if( levels[0] != nullptr ) // This can be null if stopServer happens very quickly due to network error { +#ifndef MINECRAFT_SERVER_BUILD levels[0]->saveToDisc(Minecraft::GetInstance()->progressRenderer, false); +#else + levels[0]->saveToDisc(nullptr, false); +#endif } } } @@ -2005,7 +2050,11 @@ void MinecraftServer::run(int64_t seed, void *lpParameter) app.EnterSaveNotificationSection(); if (players != nullptr) { - players->saveAll(Minecraft::GetInstance()->progressRenderer); +#ifndef MINECRAFT_SERVER_BUILD + players->saveAll(Minecraft::GetInstance()->progressRenderer); +#else + players->saveAll(nullptr); +#endif } players->broadcastAll(std::make_shared(20)); @@ -2017,7 +2066,11 @@ void MinecraftServer::run(int64_t seed, void *lpParameter) // with the data from the nethers leveldata. // Fix for #7418 - Functional: Gameplay: Saving after sleeping in a bed will place player at nighttime when restarting. ServerLevel *level = levels[levels.length - 1 - j]; +#ifndef MINECRAFT_SERVER_BUILD level->save(true, Minecraft::GetInstance()->progressRenderer, (eAction==eXuiServerAction_AutoSaveGame)); +#else + level->save(true, nullptr, (eAction == eXuiServerAction_AutoSaveGame)); +#endif players->broadcastAll(std::make_shared(33 + (j * 33))); } @@ -2025,7 +2078,11 @@ void MinecraftServer::run(int64_t seed, void *lpParameter) { saveGameRules(); - levels[0]->saveToDisc(Minecraft::GetInstance()->progressRenderer, (eAction==eXuiServerAction_AutoSaveGame)); +#ifndef MINECRAFT_SERVER_BUILD + levels[0]->saveToDisc(Minecraft::GetInstance()->progressRenderer, (eAction == eXuiServerAction_AutoSaveGame)); +#else + levels[0]->saveToDisc(nullptr, (eAction == eXuiServerAction_AutoSaveGame)); +#endif } app.LeaveSaveNotificationSection(); break; diff --git a/Minecraft.Client/Minimap.cpp b/Minecraft.Client/Minimap.cpp index b62a8a67..68780112 100644 --- a/Minecraft.Client/Minimap.cpp +++ b/Minecraft.Client/Minimap.cpp @@ -8,15 +8,18 @@ #include "../Minecraft.World/net.minecraft.world.level.saveddata.h" #include "../Minecraft.World/net.minecraft.world.level.material.h" +#ifndef MINECRAFT_SERVER_BUILD #ifdef __ORBIS__ short Minimap::LUT[256]; // 4J added #else int Minimap::LUT[256]; // 4J added #endif bool Minimap::genLUT = true; // 4J added +#endif Minimap::Minimap(Font *font, Options *options, Textures *textures, bool optimised) { +#ifndef MINECRAFT_SERVER_BUILD #ifdef __PS3__ // we're using the RSX now to upload textures to vram, so we need the main ram textures allocated from io space this->pixels = intArray((int*)RenderManager.allocIOMem(w*h*sizeof(int)), 16*16); @@ -39,7 +42,6 @@ Minimap::Minimap(Font *font, Options *options, Textures *textures, bool optimise { pixels[i] = 0x00000000; } - // 4J added - generate the colour mapping that we'll be needing as a LUT to minimise processing we actually need to do during normal rendering if( genLUT ) { @@ -47,10 +49,13 @@ Minimap::Minimap(Font *font, Options *options, Textures *textures, bool optimise } renderCount = 0; // 4J added m_optimised = optimised; +#endif } void Minimap::reloadColours() { +#ifndef MINECRAFT_SERVER_BUILD + ColourTable *colourTable = Minecraft::GetInstance()->getColourTable(); // 4J note that this code has been extracted pretty much as it was in Minimap::render, although with some byte order changes for( int i = 0; i < (14 * 4); i++ ) // 14 material colours currently, 4 brightnesses of each @@ -95,11 +100,13 @@ void Minimap::reloadColours() } genLUT = false; +#endif } // 4J added entityId void Minimap::render(shared_ptr player, Textures *textures, shared_ptr data, int entityId) { +#ifndef MINECRAFT_SERVER_BUILD // 4J - only update every 8 renders, as an optimisation // We don't want to use this for ItemFrame renders of maps, as then we can't have different maps together if( !m_optimised || ( renderCount & 7 ) == 0 ) @@ -252,5 +259,6 @@ void Minimap::render(shared_ptr player, Textures *textures, shared_ptrrCol = 1.0f; + this->gCol = 1.0f; + this->bCol = 1.0f; + + this->xd = 0.0; + this->yd = 0.0; + this->zd = 0.0; + this->gravity = 0.0f; + + this->lifetime = 30; + this->alpha = 0.5; +} + +MobAppearanceParticle::~MobAppearanceParticle() {} + + +int MobAppearanceParticle::getParticleTexture() +{ + return 3; +} + +eINSTANCEOF MobAppearanceParticle::GetType() +{ + + return (eINSTANCEOF)eTYPE_ELDER_GUARDIAN; +} + +void MobAppearanceParticle::tick() +{ + if (guardianEntity == nullptr) + { + auto g = std::make_shared(level); + g->setElderClient(); + guardianEntity = g; + } + Particle::tick(); +} + + +void MobAppearanceParticle::render( + Tesselator* /*t*/, + float partialTicks, + float /*xa*/, + float /*ya*/, + float /*za*/, + float /*xa2*/, + float /*za2*/) +{ + if (guardianEntity == nullptr) + return; + + shared_ptr cameraEntity = EntityRenderDispatcher::instance->cameraEntity; + if (cameraEntity == nullptr) + return; + + + { + double dx = cameraEntity->x - x; + double dy = cameraEntity->y - y; + double dz = cameraEntity->z - z; + if (dx*dx + dy*dy + dz*dz > 4.0 * 4.0) + return; + } + + float progress = ((float)age + partialTicks) / (float)lifetime; + float alpha = 0.05f + 0.5f * sinf(progress * (float)PI); + const float scale = 1.0f / 2.35f; + const float fullBright = 240.0f; + + + double camX = cameraEntity->xOld + (cameraEntity->x - cameraEntity->xOld) * partialTicks; + double camY = cameraEntity->yOld + (cameraEntity->y - cameraEntity->yOld) * partialTicks; + double camZ = cameraEntity->zOld + (cameraEntity->z - cameraEntity->zOld) * partialTicks; + + + float camYaw = cameraEntity->yRotO + (cameraEntity->yRot - cameraEntity->yRotO) * partialTicks; + float camPitch = cameraEntity->xRotO + (cameraEntity->xRot - cameraEntity->xRotO) * partialTicks; + + + double savedXOff = EntityRenderDispatcher::xOff; + double savedYOff = EntityRenderDispatcher::yOff; + double savedZOff = EntityRenderDispatcher::zOff; + + EntityRenderDispatcher::xOff = camX; + EntityRenderDispatcher::yOff = camY; + EntityRenderDispatcher::zOff = camZ; + + glMultiTexCoord2f(GL_TEXTURE1, fullBright, fullBright); + glDepthMask(true); + glEnable(GL_BLEND); + glEnable(GL_DEPTH_TEST); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + glPushMatrix(); + + + float px = (float)(camX - Particle::xOff); + float py = (float)(camY - Particle::yOff); + float pz = (float)(camZ - Particle::zOff); + glTranslatef(px, py, pz); + + glColor4f(1.0f, 1.0f, 1.0f, alpha); + + + glTranslatef(0.0f, 0.0f, 0.0f); + glRotatef(180.0f - camYaw, 0.0f, 1.0f, 0.0f); + glRotatef(60.0f - 150.0f * progress - camPitch, 1.0f, 0.0f, 0.0f); + glTranslatef(0.0f, -0.4f, -1.5f); + glScalef(scale, scale, scale); + + guardianEntity->yRot = guardianEntity->yRotO = 0.0f; + guardianEntity->xRot = guardianEntity->xRotO = 0.0f; + + + EntityRenderDispatcher::instance->render( + guardianEntity, + 0.0, 0.0, 0.0, + 0.0f, + partialTicks); + + glPopMatrix(); + + EntityRenderDispatcher::xOff = savedXOff; + EntityRenderDispatcher::yOff = savedYOff; + EntityRenderDispatcher::zOff = savedZOff; + + glEnable(GL_DEPTH_TEST); + glDepthMask(true); +} + + +shared_ptr MobAppearanceParticle::Provider::createParticle( + ePARTICLE_TYPE /*type*/, + Level* level, + double x, + double y, + double z, + arrayWithLength /*params*/) +{ + return std::make_shared(level, x, y, z); +} \ No newline at end of file diff --git a/Minecraft.Client/MobAppearanceParticle.h b/Minecraft.Client/MobAppearanceParticle.h new file mode 100644 index 00000000..52d36918 --- /dev/null +++ b/Minecraft.Client/MobAppearanceParticle.h @@ -0,0 +1,42 @@ +#pragma once +#include "Particle.h" +#include "Tesselator.h" +#include "../Minecraft.World/Guardian.h" + + +class MobAppearanceParticle : public Particle +{ +public: + class Provider + { + public: + shared_ptr createParticle( + ePARTICLE_TYPE type, + Level* level, + double x, + double y, + double z, + arrayWithLength params); + }; + + MobAppearanceParticle(Level* level, double x, double y, double z); + virtual ~MobAppearanceParticle(); + + virtual int getParticleTexture() override; + + virtual eINSTANCEOF GetType() override; + + virtual void tick() override; + + virtual void render( + Tesselator* t, + float a, + float xa, + float ya, + float za, + float xa2, + float za2) override; + +private: + shared_ptr guardianEntity; +}; \ No newline at end of file diff --git a/Minecraft.Client/PlayerConnection.cpp b/Minecraft.Client/PlayerConnection.cpp index f5bbd87e..166e03dc 100644 --- a/Minecraft.Client/PlayerConnection.cpp +++ b/Minecraft.Client/PlayerConnection.cpp @@ -38,6 +38,17 @@ // 4J Added #include "../Minecraft.World/net.minecraft.world.item.crafting.h" #include "Options.h" + +//neo: Command Includes +#include "TeleportCommand.h" +#include "../Minecraft.World/GiveItemCommand.h" +#include "../Minecraft.World/TimeCommand.h" +#include "../Minecraft.World/KillCommand.h" +#include "../Minecraft.World/GameModeCommand.h" +#include "../Minecraft.World/ToggleDownfallCommand.h" + +#include + #if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD) #include "../Minecraft.Server/ServerLogManager.h" #include "../Minecraft.Server/Access/Access.h" @@ -48,6 +59,10 @@ extern bool g_Win64DedicatedServer; #endif +//neo: added +#include "ItemNameMap.h" +#include "../Minecraft.World/ByteArrayOutputStream.h" + namespace { // Anti-cheat thresholds. Keep server-side checks authoritative even in host mode. @@ -1026,10 +1041,319 @@ void PlayerConnection::handleCommand(const wstring& message) if (FourKitBridge::HandlePlayerCommand(player->entityId, commandLine)) return; #endif - // 4J - TODO -#if 0 - server.getCommandDispatcher().performCommand(player, message); -#endif + wstringstream ss(message.substr(1)); + wstring cmd; + ss >> cmd; +if (cmd == L"tp" || cmd == L"teleport") +{ + if (!player->hasPermission(eGameCommand_Teleport)) + { + warn(L"You do not have permission to use this command."); + return; + } + + wstring arg1, arg2, arg3, arg4, arg5, arg6; + ss >> arg1 >> arg2 >> arg3 >> arg4 >> arg5 >> arg6; + shared_ptr target; + shared_ptr destination; + if (arg1.empty()) + { + warn(L"Usage: /tp [player] "); + warn(L"Usage: /tp [player] [y_rot] [x_rot]"); + return; + } + + auto isCoord = [](const wstring& s) -> bool { + if (s.empty()) return false; + for (size_t i = 0; i < s.size(); i++) + if (!iswdigit(s[i]) && s[i] != L'-' && s[i] != L'.') return false; + return true; + }; + + bool arg2IsCoord = isCoord(arg2); + if (!arg2IsCoord && !arg2.empty()) + { + target = server->getPlayers()->getPlayer(arg1); + destination = server->getPlayers()->getPlayer(arg2); + if (target && destination) + { + shared_ptr packet = TeleportCommand::preparePacket( + target->getXuid(), destination->getXuid()); + server->getCommandDispatcher()->performCommand( + player, eGameCommand_Teleport, packet->data); + } + else + { + warn(L"Player not found."); + } + } + else + { + wstring sx, sy, sz, sYRot, sXRot; + shared_ptr tpTarget; + if (arg2IsCoord) + { + tpTarget = player; + sx = arg1; + sy = arg2; + sz = arg3; + sYRot = arg4; + sXRot = arg5; + } + else + { + tpTarget = server->getPlayers()->getPlayer(arg1); + sx = arg2; + sy = arg3; + sz = arg4; + sYRot = arg5; + sXRot = arg6; + } + + if (!tpTarget) + { + warn(L"Player not found."); + return; + } + + if (sx.empty() || sy.empty() || sz.empty()) + { + warn(L"Usage: /tp [player] [y_rot] [x_rot]"); + return; + } + + float x = stof(sx); + float y = stof(sy); + float z = stof(sz); + byte yRot = sYRot.empty() + ? static_cast(tpTarget->yRot) + : static_cast(stoi(sYRot) & 0xFF); + byte xRot = sXRot.empty() + ? static_cast(tpTarget->xRot) + : static_cast(stoi(sXRot) & 0xFF); + + shared_ptr gamePacket = TeleportCommand::preparePacket( + tpTarget->getXuid(), x, y, z, yRot, xRot); + server->getCommandDispatcher()->performCommand(tpTarget, eGameCommand_Teleport, gamePacket->data); + } +} else if (cmd == L"time") +{ + if (!player->hasPermission(eGameCommand_Time)) + { + warn(L"You do not have permission to use this command."); + return; + } + + wstring action; + ss >> action; + if (action.empty()) + { + warn(L"Usage: /time ..."); + warn(L" /time set "); + warn(L" /time add "); + warn(L" /time query "); + return; + } + + if (action == L"set") + { + wstring timeVal; + ss >> timeVal; + if (timeVal.empty()) + { + warn(L"Usage: /time set "); + return; + } + + static const unordered_map namedTimes = { + { L"day", 1000 }, + { L"noon", 6000 }, + { L"sunset", 12000 }, + { L"night", 13000 }, + { L"midnight", 18000 }, + { L"sunrise", 23000 }, + }; + + int ticks = -1; + auto it = namedTimes.find(timeVal); + if (it != namedTimes.end()) + { + ticks = it->second; + } + else + { + try { + size_t pos; + ticks = stoi(timeVal, &pos); + if (pos != timeVal.size() || ticks < 0 || ticks > 24000) + { + warn(L"Time value must be between 0 and 24000, or a named time."); + return; + } + } + catch (...) { + warn(L"Unknown time value: " + timeVal); + warn(L"Usage: /time set "); + return; + } + } + + shared_ptr packet = TimeCommand::preparePacket(ticks); + server->getCommandDispatcher()->performCommand(player, eGameCommand_Time, packet->data); + info(L"Time set to " + timeVal + L" (" + to_wstring(ticks) + L" ticks)."); + } + else if (action == L"add") + { + wstring amountStr; + ss >> amountStr; + if (amountStr.empty()) + { + warn(L"Usage: /time add "); + return; + } + + try { + size_t pos; + int amount = stoi(amountStr, &pos); + if (pos != amountStr.size() || amount < 1) + { + warn(L"Amount must be a positive integer."); + return; + } + + int currentTicks = server->getCommandSenderWorld()->getTimeOfDay(0) * 1000; + int newTicks = (currentTicks + amount) % 24000; + shared_ptr packet = TimeCommand::preparePacket(newTicks); + server->getCommandDispatcher()->performCommand(player, eGameCommand_Time, packet->data); + info(L"Added " + to_wstring(amount) + L" ticks. Time is now " + to_wstring(newTicks) + L"."); + } + catch (...) { + warn(L"Invalid amount: " + amountStr); + } + } + else if (action == L"query") + { + wstring queryType; + ss >> queryType; + if (queryType.empty()) + { + warn(L"Usage: /time query "); + return; + } + + int currentTicks = server->getCommandSenderWorld()->getTimeOfDay(0) * 1000; + if (queryType == L"daytime") + { + info(L"The current daytime is " + to_wstring(currentTicks % 24000) + L" ticks."); + } + else if (queryType == L"gametime") + { + info(L"The total game time is " + to_wstring(currentTicks) + L" ticks."); + } + else if (queryType == L"day") + { + info(L"The current day is " + to_wstring(currentTicks / 24000) + L"."); + } + else + { + warn(L"Unknown query type: " + queryType); + warn(L"Usage: /time query "); + } + } + else + { + warn(L"Unknown action: " + action); + warn(L"Usage: /time ..."); + } +} + else if (cmd == L"kill") + { + if (!player->hasPermission(eGameCommand_Kill)) + { + warn(L"You do not have permission to use this command."); + return; + } + server->getCommandDispatcher()->performCommand(player, eGameCommand_Kill, byteArray()); + } + else if (cmd == L"toggledownfall") + { + if (!player->hasPermission(eGameCommand_ToggleDownfall)) + { + warn(L"You do not have permission to use this command."); + return; + } + shared_ptr packet = ToggleDownfallCommand::preparePacket(); + server->getCommandDispatcher()->performCommand(player, eGameCommand_ToggleDownfall, packet->data); + } else if (cmd == L"gamemode") { + if (!player->hasPermission(eGameCommand_GameMode)) + { + warn(L"You do not have permission to use this command."); + return; + } + wstring modeStr, targetName; + ss >> modeStr >> targetName; + if (modeStr.empty()) { + warn(L"Usage: /gamemode [player]"); + return; + } + + int mode = -1; + if (modeStr == L"0" || modeStr == L"s" || modeStr == L"survival") + mode = 0; + else if (modeStr == L"1" || modeStr == L"c" || modeStr == L"creative") + mode = 1; + else if (modeStr == L"2" || modeStr == L"a" || modeStr == L"adventure") + mode = 2; + else { + warn(L"Unknown game mode: " + modeStr); + return; + } + + shared_ptr target; + if (targetName.empty()) { + target = player; + } else { + target = server->getPlayers()->getPlayer(targetName); + if (!target) { + warn(L"Player not found: " + targetName); + return; + } + } + + shared_ptr packet = GameModeCommand::preparePacket(target, mode); + server->getCommandDispatcher()->performCommand(player, eGameCommand_GameMode, packet->data); + } else if (cmd == L"give") { + if (!player->hasPermission(eGameCommand_Give)) + { + warn(L"You do not have permission to use this command."); + return; + } + wstring targetName, itemStr, amountStr, auxStr; + ss >> targetName >> itemStr >> amountStr >> auxStr; + if (targetName.empty() || itemStr.empty()) { + warn(L"Usage: /give |minecraft: [amount] [data]"); + return; + } + + shared_ptr target = server->getPlayers()->getPlayer(targetName); + if (!target) { + warn(L"Player not found: " + targetName); + return; + } + int item = 0; + int amount = 1, aux = 0; + try { + item = itemStr.find(L"minecraft:") == 0 ? GetItemIdByName(itemStr.substr(10)) : std::stoi(itemStr); + if (!amountStr.empty()) amount = std::stoi(amountStr); + if (!auxStr.empty()) aux = std::stoi(auxStr); + } catch (...) { + warn(L"Invalid item ID/Name or amount"); + return; + } + + shared_ptr packet = GiveItemCommand::preparePacket(target, item, amount, aux); + server->getCommandDispatcher()->performCommand(player, eGameCommand_Give, packet->data); + } } void PlayerConnection::handleAnimate(shared_ptr packet) @@ -1127,14 +1451,12 @@ int PlayerConnection::countDelayedPackets() void PlayerConnection::info(const wstring& string) { - // 4J-PB - removed, since it needs to be localised in the language the client is in - //send( shared_ptr( new ChatPacket(L"�7" + string) ) ); + send( shared_ptr( new ChatPacket(L"§7" + string) ) ); } void PlayerConnection::warn(const wstring& string) { - // 4J-PB - removed, since it needs to be localised in the language the client is in - //send( shared_ptr( new ChatPacket(L"�9" + string) ) ); + send( shared_ptr( new ChatPacket(L"§c" + string) ) ); } wstring PlayerConnection::getConsoleName() diff --git a/Minecraft.Client/TeleportCommand.cpp b/Minecraft.Client/TeleportCommand.cpp index ebe42efc..13edbe4f 100644 --- a/Minecraft.Client/TeleportCommand.cpp +++ b/Minecraft.Client/TeleportCommand.cpp @@ -19,89 +19,95 @@ EGameCommand TeleportCommand::getId() void TeleportCommand::execute(shared_ptr source, byteArray commandData) { - ByteArrayInputStream bais(commandData); - DataInputStream dis(&bais); + ByteArrayInputStream bais(commandData); + DataInputStream dis(&bais); + byte flag = dis.readByte(); + PlayerUID subjectID = dis.readPlayerUID(); + PlayerList *players = MinecraftServer::getInstance()->getPlayerList(); + shared_ptr subject = players->getPlayer(subjectID); + if (subject == nullptr || !subject->isAlive()) + return; - PlayerUID subjectID = dis.readPlayerUID(); - PlayerUID destinationID = dis.readPlayerUID(); - - bais.reset(); + if (flag == 1) + { + float x = dis.readFloat(); + float y = dis.readFloat(); + float z = dis.readFloat(); + byte yRot = dis.readByte(); + byte xRot = dis.readByte(); - PlayerList *players = MinecraftServer::getInstance()->getPlayerList(); - - shared_ptr subject = players->getPlayer(subjectID); - shared_ptr destination = players->getPlayer(destinationID); - - if(subject != nullptr && destination != nullptr && subject->level->dimension->id == destination->level->dimension->id && subject->isAlive() ) - { - subject->ride(nullptr); + subject->ride(nullptr); #if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD) - { - double outX, outY, outZ; - bool cancelled = FourKitBridge::FirePlayerTeleport(subject->entityId, - subject->x, subject->y, subject->z, subject->dimension, - destination->x, destination->y, destination->z, destination->dimension, - 1 /* COMMAND */, - &outX, &outY, &outZ); - if (cancelled) - return; - subject->connection->teleport(outX, outY, outZ, destination->yRot, destination->xRot); - } + { + double outX, outY, outZ; + bool cancelled = FourKitBridge::FirePlayerTeleport(subject->entityId, + subject->x, subject->y, subject->z, subject->dimension, + x, y, z, subject->dimension, + 1, &outX, &outY, &outZ); + if (cancelled) + return; + subject->connection->teleport(outX, outY, outZ, yRot, xRot); + } #else - subject->connection->teleport(destination->x, destination->y, destination->z, destination->yRot, destination->xRot); + subject->connection->teleport(x, y, z, yRot, xRot); #endif - //logAdminAction(source, "commands.tp.success", subject->getAName(), destination->getAName()); - logAdminAction(source, ChatPacket::e_ChatCommandTeleportSuccess, subject->getName(), eTYPE_SERVERPLAYER, destination->getName()); + logAdminAction(source, ChatPacket::e_ChatCommandTeleportSuccess, subject->getName(), eTYPE_SERVERPLAYER, subject->getName()); + } + else + { + PlayerUID destinationID = dis.readPlayerUID(); + shared_ptr destination = players->getPlayer(destinationID); + if (destination == nullptr) + return; - if(subject == source) - { - destination->sendMessage(subject->getName(), ChatPacket::e_ChatCommandTeleportToMe); - } - else - { - subject->sendMessage(destination->getName(), ChatPacket::e_ChatCommandTeleportMe); - } - } + if (subject->level->dimension->id != destination->level->dimension->id) + return; - //if (args.length >= 1) { - // MinecraftServer server = MinecraftServer.getInstance(); - // ServerPlayer victim; - - // if (args.length == 2 || args.length == 4) { - // victim = server.getPlayers().getPlayer(args[0]); - // if (victim == null) throw new PlayerNotFoundException(); - // } else { - // victim = (ServerPlayer) convertSourceToPlayer(source); - // } - - // if (args.length == 3 || args.length == 4) { - // if (victim.level != null) { - // int pos = args.length - 3; - // int maxPos = Level.MAX_LEVEL_SIZE; - // int x = convertArgToInt(source, args[pos++], -maxPos, maxPos); - // int y = convertArgToInt(source, args[pos++], Level.minBuildHeight, Level.maxBuildHeight); - // int z = convertArgToInt(source, args[pos++], -maxPos, maxPos); - - // victim.teleportTo(x + 0.5f, y, z + 0.5f); - // logAdminAction(source, "commands.tp.coordinates", victim.getAName(), x, y, z); - // } - // } else if (args.length == 1 || args.length == 2) { - // ServerPlayer destination = server.getPlayers().getPlayer(args[args.length - 1]); - // if (destination == null) throw new PlayerNotFoundException(); - - // victim.connection.teleport(destination.x, destination.y, destination.z, destination.yRot, destination.xRot); - // logAdminAction(source, "commands.tp.success", victim.getAName(), destination.getAName()); - // } - //} + subject->ride(nullptr); +#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD) + { + double outX, outY, outZ; + bool cancelled = FourKitBridge::FirePlayerTeleport(subject->entityId, + subject->x, subject->y, subject->z, subject->dimension, + destination->x, destination->y, destination->z, destination->dimension, + 1, &outX, &outY, &outZ); + if (cancelled) + return; + subject->connection->teleport(outX, outY, outZ, destination->yRot, destination->xRot); + } +#else + subject->connection->teleport(destination->x, destination->y, destination->z, destination->yRot, destination->xRot); +#endif + logAdminAction(source, ChatPacket::e_ChatCommandTeleportSuccess, subject->getName(), eTYPE_SERVERPLAYER, destination->getName()); + if (subject == source) + destination->sendMessage(subject->getName(), ChatPacket::e_ChatCommandTeleportToMe); + else + subject->sendMessage(destination->getName(), ChatPacket::e_ChatCommandTeleportMe); + } } shared_ptr TeleportCommand::preparePacket(PlayerUID subject, PlayerUID destination) { - ByteArrayOutputStream baos; - DataOutputStream dos(&baos); + ByteArrayOutputStream baos; + DataOutputStream dos(&baos); + dos.writeByte(0); + dos.writePlayerUID(subject); + dos.writePlayerUID(destination); + return std::make_shared(eGameCommand_Teleport, baos.toByteArray()); +} - dos.writePlayerUID(subject); - dos.writePlayerUID(destination); - return std::make_shared(eGameCommand_Teleport, baos.toByteArray()); +//neo: added +shared_ptr TeleportCommand::preparePacket(PlayerUID subject, float x, float y, float z, byte yRot, byte xRot) +{ + ByteArrayOutputStream baos; + DataOutputStream dos(&baos); + dos.writeByte(1); + dos.writePlayerUID(subject); + dos.writeFloat(x); + dos.writeFloat(y); + dos.writeFloat(z); + dos.writeByte(yRot); + dos.writeByte(xRot); + return std::make_shared(eGameCommand_Teleport, baos.toByteArray()); } \ No newline at end of file diff --git a/Minecraft.Client/TeleportCommand.h b/Minecraft.Client/TeleportCommand.h index 0b37e6d1..a0fb7f1c 100644 --- a/Minecraft.Client/TeleportCommand.h +++ b/Minecraft.Client/TeleportCommand.h @@ -9,4 +9,7 @@ public: virtual void execute(shared_ptr source, byteArray commandData); static shared_ptr preparePacket(PlayerUID subject, PlayerUID destination); + + //neo: added + static shared_ptr preparePacket(PlayerUID subject, float x, float y, float z, byte yRot, byte xRot); }; \ No newline at end of file diff --git a/Minecraft.Client/Windows64/4JLibs b/Minecraft.Client/Windows64/4JLibs index f567d33c..8fb036f6 160000 --- a/Minecraft.Client/Windows64/4JLibs +++ b/Minecraft.Client/Windows64/4JLibs @@ -1 +1 @@ -Subproject commit f567d33ccbddad1d8356e4d8344b22de80f23ab2 +Subproject commit 8fb036f6d6ca5aa5aa2e20633638d6232a58d508 diff --git a/Minecraft.Client/Windows64/Windows64_Minecraft.cpp b/Minecraft.Client/Windows64/Windows64_Minecraft.cpp index 809c64e3..29986a85 100644 --- a/Minecraft.Client/Windows64/Windows64_Minecraft.cpp +++ b/Minecraft.Client/Windows64/Windows64_Minecraft.cpp @@ -803,7 +803,7 @@ BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) AdjustWindowRect(&wr, WS_OVERLAPPEDWINDOW, FALSE); // adjust the size g_hWnd = CreateWindowW( L"MinecraftClass", - L"Minecraft", + L"Minecraft: neoLegacy", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, diff --git a/Minecraft.Client/Windows64Media/Sound/Minecraft/mob/guardian/attack_loop.ogg b/Minecraft.Client/Windows64Media/Sound/Minecraft/mob/guardian/attack_loop.ogg new file mode 100644 index 00000000..b070c156 Binary files /dev/null and b/Minecraft.Client/Windows64Media/Sound/Minecraft/mob/guardian/attack_loop.ogg differ diff --git a/Minecraft.Client/Windows64Media/Sound/Minecraft/mob/guardian/curse.ogg b/Minecraft.Client/Windows64Media/Sound/Minecraft/mob/guardian/curse.ogg new file mode 100644 index 00000000..557ac3d9 Binary files /dev/null and b/Minecraft.Client/Windows64Media/Sound/Minecraft/mob/guardian/curse.ogg differ diff --git a/Minecraft.Client/Windows64Media/Sound/Minecraft/mob/guardian/elder_death.ogg b/Minecraft.Client/Windows64Media/Sound/Minecraft/mob/guardian/elder_death.ogg new file mode 100644 index 00000000..139c836c Binary files /dev/null and b/Minecraft.Client/Windows64Media/Sound/Minecraft/mob/guardian/elder_death.ogg differ diff --git a/Minecraft.Client/Windows64Media/Sound/Minecraft/mob/guardian/elder_hit1.ogg b/Minecraft.Client/Windows64Media/Sound/Minecraft/mob/guardian/elder_hit1.ogg new file mode 100644 index 00000000..b38ccca5 Binary files /dev/null and b/Minecraft.Client/Windows64Media/Sound/Minecraft/mob/guardian/elder_hit1.ogg differ diff --git a/Minecraft.Client/Windows64Media/Sound/Minecraft/mob/guardian/elder_hit2.ogg b/Minecraft.Client/Windows64Media/Sound/Minecraft/mob/guardian/elder_hit2.ogg new file mode 100644 index 00000000..84a6968f Binary files /dev/null and b/Minecraft.Client/Windows64Media/Sound/Minecraft/mob/guardian/elder_hit2.ogg differ diff --git a/Minecraft.Client/Windows64Media/Sound/Minecraft/mob/guardian/elder_hit3.ogg b/Minecraft.Client/Windows64Media/Sound/Minecraft/mob/guardian/elder_hit3.ogg new file mode 100644 index 00000000..de18bacc Binary files /dev/null and b/Minecraft.Client/Windows64Media/Sound/Minecraft/mob/guardian/elder_hit3.ogg differ diff --git a/Minecraft.Client/Windows64Media/Sound/Minecraft/mob/guardian/elder_hit4.ogg b/Minecraft.Client/Windows64Media/Sound/Minecraft/mob/guardian/elder_hit4.ogg new file mode 100644 index 00000000..5aff9102 Binary files /dev/null and b/Minecraft.Client/Windows64Media/Sound/Minecraft/mob/guardian/elder_hit4.ogg differ diff --git a/Minecraft.Client/Windows64Media/Sound/Minecraft/mob/guardian/elder_idle1.ogg b/Minecraft.Client/Windows64Media/Sound/Minecraft/mob/guardian/elder_idle1.ogg new file mode 100644 index 00000000..ddc8acaa Binary files /dev/null and b/Minecraft.Client/Windows64Media/Sound/Minecraft/mob/guardian/elder_idle1.ogg differ diff --git a/Minecraft.Client/Windows64Media/Sound/Minecraft/mob/guardian/elder_idle2.ogg b/Minecraft.Client/Windows64Media/Sound/Minecraft/mob/guardian/elder_idle2.ogg new file mode 100644 index 00000000..e1d1cfd9 Binary files /dev/null and b/Minecraft.Client/Windows64Media/Sound/Minecraft/mob/guardian/elder_idle2.ogg differ diff --git a/Minecraft.Client/Windows64Media/Sound/Minecraft/mob/guardian/elder_idle3.ogg b/Minecraft.Client/Windows64Media/Sound/Minecraft/mob/guardian/elder_idle3.ogg new file mode 100644 index 00000000..a97f3290 Binary files /dev/null and b/Minecraft.Client/Windows64Media/Sound/Minecraft/mob/guardian/elder_idle3.ogg differ diff --git a/Minecraft.Client/Windows64Media/Sound/Minecraft/mob/guardian/elder_idle4.ogg b/Minecraft.Client/Windows64Media/Sound/Minecraft/mob/guardian/elder_idle4.ogg new file mode 100644 index 00000000..d299ca57 Binary files /dev/null and b/Minecraft.Client/Windows64Media/Sound/Minecraft/mob/guardian/elder_idle4.ogg differ diff --git a/Minecraft.Client/Windows64Media/Sound/Minecraft/mob/guardian/flop1.ogg b/Minecraft.Client/Windows64Media/Sound/Minecraft/mob/guardian/flop1.ogg new file mode 100644 index 00000000..8961fb97 Binary files /dev/null and b/Minecraft.Client/Windows64Media/Sound/Minecraft/mob/guardian/flop1.ogg differ diff --git a/Minecraft.Client/Windows64Media/Sound/Minecraft/mob/guardian/flop2.ogg b/Minecraft.Client/Windows64Media/Sound/Minecraft/mob/guardian/flop2.ogg new file mode 100644 index 00000000..49141eb1 Binary files /dev/null and b/Minecraft.Client/Windows64Media/Sound/Minecraft/mob/guardian/flop2.ogg differ diff --git a/Minecraft.Client/Windows64Media/Sound/Minecraft/mob/guardian/flop3.ogg b/Minecraft.Client/Windows64Media/Sound/Minecraft/mob/guardian/flop3.ogg new file mode 100644 index 00000000..5113ebb4 Binary files /dev/null and b/Minecraft.Client/Windows64Media/Sound/Minecraft/mob/guardian/flop3.ogg differ diff --git a/Minecraft.Client/Windows64Media/Sound/Minecraft/mob/guardian/flop4.ogg b/Minecraft.Client/Windows64Media/Sound/Minecraft/mob/guardian/flop4.ogg new file mode 100644 index 00000000..f4b5d78c Binary files /dev/null and b/Minecraft.Client/Windows64Media/Sound/Minecraft/mob/guardian/flop4.ogg differ diff --git a/Minecraft.Client/Windows64Media/Sound/Minecraft/mob/guardian/guardian_death.ogg b/Minecraft.Client/Windows64Media/Sound/Minecraft/mob/guardian/guardian_death.ogg new file mode 100644 index 00000000..5610433f Binary files /dev/null and b/Minecraft.Client/Windows64Media/Sound/Minecraft/mob/guardian/guardian_death.ogg differ diff --git a/Minecraft.Client/Windows64Media/Sound/Minecraft/mob/guardian/guardian_hit1.ogg b/Minecraft.Client/Windows64Media/Sound/Minecraft/mob/guardian/guardian_hit1.ogg new file mode 100644 index 00000000..1eee75a1 Binary files /dev/null and b/Minecraft.Client/Windows64Media/Sound/Minecraft/mob/guardian/guardian_hit1.ogg differ diff --git a/Minecraft.Client/Windows64Media/Sound/Minecraft/mob/guardian/guardian_hit2.ogg b/Minecraft.Client/Windows64Media/Sound/Minecraft/mob/guardian/guardian_hit2.ogg new file mode 100644 index 00000000..8a26539e Binary files /dev/null and b/Minecraft.Client/Windows64Media/Sound/Minecraft/mob/guardian/guardian_hit2.ogg differ diff --git a/Minecraft.Client/Windows64Media/Sound/Minecraft/mob/guardian/guardian_hit3.ogg b/Minecraft.Client/Windows64Media/Sound/Minecraft/mob/guardian/guardian_hit3.ogg new file mode 100644 index 00000000..d7c4a6b4 Binary files /dev/null and b/Minecraft.Client/Windows64Media/Sound/Minecraft/mob/guardian/guardian_hit3.ogg differ diff --git a/Minecraft.Client/Windows64Media/Sound/Minecraft/mob/guardian/guardian_hit4.ogg b/Minecraft.Client/Windows64Media/Sound/Minecraft/mob/guardian/guardian_hit4.ogg new file mode 100644 index 00000000..393bf95e Binary files /dev/null and b/Minecraft.Client/Windows64Media/Sound/Minecraft/mob/guardian/guardian_hit4.ogg differ diff --git a/Minecraft.Client/Windows64Media/Sound/Minecraft/mob/guardian/land_death.ogg b/Minecraft.Client/Windows64Media/Sound/Minecraft/mob/guardian/land_death.ogg new file mode 100644 index 00000000..51835b3c Binary files /dev/null and b/Minecraft.Client/Windows64Media/Sound/Minecraft/mob/guardian/land_death.ogg differ diff --git a/Minecraft.Client/Windows64Media/Sound/Minecraft/mob/guardian/land_hit1.ogg b/Minecraft.Client/Windows64Media/Sound/Minecraft/mob/guardian/land_hit1.ogg new file mode 100644 index 00000000..1283a8aa Binary files /dev/null and b/Minecraft.Client/Windows64Media/Sound/Minecraft/mob/guardian/land_hit1.ogg differ diff --git a/Minecraft.Client/Windows64Media/Sound/Minecraft/mob/guardian/land_hit2.ogg b/Minecraft.Client/Windows64Media/Sound/Minecraft/mob/guardian/land_hit2.ogg new file mode 100644 index 00000000..7216d50a Binary files /dev/null and b/Minecraft.Client/Windows64Media/Sound/Minecraft/mob/guardian/land_hit2.ogg differ diff --git a/Minecraft.Client/Windows64Media/Sound/Minecraft/mob/guardian/land_hit3.ogg b/Minecraft.Client/Windows64Media/Sound/Minecraft/mob/guardian/land_hit3.ogg new file mode 100644 index 00000000..9df9d2ab Binary files /dev/null and b/Minecraft.Client/Windows64Media/Sound/Minecraft/mob/guardian/land_hit3.ogg differ diff --git a/Minecraft.Client/Windows64Media/Sound/Minecraft/mob/guardian/land_hit4.ogg b/Minecraft.Client/Windows64Media/Sound/Minecraft/mob/guardian/land_hit4.ogg new file mode 100644 index 00000000..08538027 Binary files /dev/null and b/Minecraft.Client/Windows64Media/Sound/Minecraft/mob/guardian/land_hit4.ogg differ diff --git a/Minecraft.Client/Windows64Media/Sound/Minecraft/mob/guardian/land_idle1.ogg b/Minecraft.Client/Windows64Media/Sound/Minecraft/mob/guardian/land_idle1.ogg new file mode 100644 index 00000000..f0aeda88 Binary files /dev/null and b/Minecraft.Client/Windows64Media/Sound/Minecraft/mob/guardian/land_idle1.ogg differ diff --git a/Minecraft.Client/Windows64Media/Sound/Minecraft/mob/guardian/land_idle2.ogg b/Minecraft.Client/Windows64Media/Sound/Minecraft/mob/guardian/land_idle2.ogg new file mode 100644 index 00000000..3576b166 Binary files /dev/null and b/Minecraft.Client/Windows64Media/Sound/Minecraft/mob/guardian/land_idle2.ogg differ diff --git a/Minecraft.Client/Windows64Media/Sound/Minecraft/mob/guardian/land_idle3.ogg b/Minecraft.Client/Windows64Media/Sound/Minecraft/mob/guardian/land_idle3.ogg new file mode 100644 index 00000000..8cfbf345 Binary files /dev/null and b/Minecraft.Client/Windows64Media/Sound/Minecraft/mob/guardian/land_idle3.ogg differ diff --git a/Minecraft.Client/Windows64Media/Sound/Minecraft/mob/guardian/land_idle4.ogg b/Minecraft.Client/Windows64Media/Sound/Minecraft/mob/guardian/land_idle4.ogg new file mode 100644 index 00000000..ca0906d3 Binary files /dev/null and b/Minecraft.Client/Windows64Media/Sound/Minecraft/mob/guardian/land_idle4.ogg differ diff --git a/Minecraft.Client/cmake/sources/Common.cmake b/Minecraft.Client/cmake/sources/Common.cmake index 32fe2071..454419f9 100644 --- a/Minecraft.Client/cmake/sources/Common.cmake +++ b/Minecraft.Client/cmake/sources/Common.cmake @@ -748,6 +748,8 @@ set(_MINECRAFT_CLIENT_COMMON_NET_MINECRAFT_CLIENT_PARTICLE "${CMAKE_CURRENT_SOURCE_DIR}/WaterDropParticle.h" "${CMAKE_CURRENT_SOURCE_DIR}/ParticleType.h" "${CMAKE_CURRENT_SOURCE_DIR}/ParticleType.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/MobAppearanceParticle.h" + "${CMAKE_CURRENT_SOURCE_DIR}/MobAppearanceParticle.cpp" ) source_group("net/minecraft/client/particle" FILES ${_MINECRAFT_CLIENT_COMMON_NET_MINECRAFT_CLIENT_PARTICLE}) diff --git a/Minecraft.Server/Windows64/ServerMain.cpp b/Minecraft.Server/Windows64/ServerMain.cpp index b5d5402c..b423a90c 100644 --- a/Minecraft.Server/Windows64/ServerMain.cpp +++ b/Minecraft.Server/Windows64/ServerMain.cpp @@ -488,39 +488,11 @@ int main(int argc, char **argv) config.worldHellScale); #endif - LogStartupStep("registering hidden window class"); - HINSTANCE hInstance = GetModuleHandle(NULL); - MyRegisterClass(hInstance); - - LogStartupStep("creating hidden window"); - if (!InitInstance(hInstance, SW_HIDE)) - { - LogError("startup", "Failed to create window instance."); - - return 2; - } - ShowWindow(g_hWnd, SW_HIDE); - - LogStartupStep("initializing graphics device wrappers"); - if (FAILED(InitDevice())) - { - LogError("startup", "Failed to initialize D3D device."); - CleanupDevice(); - - return 2; - } - LogStartupStep("loading media/string tables"); app.loadMediaArchive(); - RenderManager.Initialise(g_pd3dDevice, g_pSwapChain); app.loadStringTable(); - ui.init(g_pd3dDevice, g_pImmediateContext, g_pRenderTargetView, g_pDepthStencilView, g_iScreenWidth, g_iScreenHeight); InputManager.Initialise(1, 3, MINECRAFT_ACTION_MAX, ACTION_MAX_MENU); - g_KBMInput.Init(); - DefineActions(); - InputManager.SetJoypadMapVal(0, 0); - InputManager.SetKeyRepeatRate(0.3f, 0.2f); ProfileManager.Initialise( TITLEID_MINECRAFT, diff --git a/Minecraft.Server/cmake/sources/Common.cmake b/Minecraft.Server/cmake/sources/Common.cmake index 54438f86..de12229c 100644 --- a/Minecraft.Server/cmake/sources/Common.cmake +++ b/Minecraft.Server/cmake/sources/Common.cmake @@ -4,6 +4,8 @@ get_filename_component(_MS_SRC "${CMAKE_CURRENT_LIST_DIR}/../.." ABSOLUTE) set(_MINECRAFT_SERVER_COMMON_ROOT + "${_MS_SRC}/../Minecraft.Client/MobAppearanceParticle.h" + "${_MS_SRC}/../Minecraft.Client/MobAppearanceParticle.cpp" "${_MS_SRC}/../Minecraft.Client/AbstractTexturePack.cpp" "${_MS_SRC}/../Minecraft.Client/AchievementPopup.cpp" "${_MS_SRC}/../Minecraft.Client/AchievementScreen.cpp" diff --git a/Minecraft.World/Biome.cpp b/Minecraft.World/Biome.cpp index ef58797a..f35769db 100644 --- a/Minecraft.World/Biome.cpp +++ b/Minecraft.World/Biome.cpp @@ -551,10 +551,10 @@ int Biome::getRandomDoublePlantType(Random *random) { int type = random->nextInt(10); - if (type < 7) return 0; + if (type < 7) return 2; if (type == 7) return 3; if (type == 8) return 4; - return 2; + return 5; } Biome* Biome::getBiome(uint32_t id) { diff --git a/Minecraft.World/DataOutputStream.cpp b/Minecraft.World/DataOutputStream.cpp index 1a3931cf..d8d4eb02 100644 --- a/Minecraft.World/DataOutputStream.cpp +++ b/Minecraft.World/DataOutputStream.cpp @@ -273,4 +273,10 @@ void DataOutputStream::writePlayerUID(PlayerUID player) #else writeLong(player); #endif // PS3 +} + +//neo: added +OutputStream* DataOutputStream::getChildOutputStream() +{ + return stream; } \ No newline at end of file diff --git a/Minecraft.World/DataOutputStream.h b/Minecraft.World/DataOutputStream.h index 7ecfcd44..545157ff 100644 --- a/Minecraft.World/DataOutputStream.h +++ b/Minecraft.World/DataOutputStream.h @@ -35,4 +35,7 @@ public: virtual void writeUTF(const wstring& a); virtual void writePlayerUID(PlayerUID player); virtual void flush(); + + //neo: added for future use cases, dont ask. + virtual OutputStream* getChildOutputStream(); }; \ No newline at end of file diff --git a/Minecraft.World/GameModeCommand.cpp b/Minecraft.World/GameModeCommand.cpp index 6e4cd858..ba38f070 100644 --- a/Minecraft.World/GameModeCommand.cpp +++ b/Minecraft.World/GameModeCommand.cpp @@ -1,6 +1,11 @@ #include "stdafx.h" #include "net.minecraft.commands.h" #include "GameModeCommand.h" +#include "../Minecraft.Client/MinecraftServer.h" +#include "../Minecraft.Client/ServerPlayer.h" +#include "../Minecraft.Client/PlayerList.h" +#include "LevelSettings.h" +#include "net.minecraft.network.packet.h" EGameCommand GameModeCommand::getId() { @@ -14,37 +19,34 @@ int GameModeCommand::getPermissionLevel() void GameModeCommand::execute(shared_ptr source, byteArray commandData) { - //if (args.length > 0) { - // GameType newMode = getModeForString(source, args[0]); - // Player player = args.length >= 2 ? convertToPlayer(source, args[1]) : convertSourceToPlayer(source); + ByteArrayInputStream bais(commandData); + DataInputStream dis(&bais); + PlayerUID uid = dis.readPlayerUID(); + int modeId = dis.readInt(); + shared_ptr player = MinecraftServer::getInstance()->getPlayers()->getPlayer(uid); + if (player != nullptr) + { + GameType *newMode = GameType::byId(modeId); + if (newMode != nullptr) + { + player->setGameMode(newMode); + player->resetLastActionTime(); + source->sendMessage(L"Set " + player->getName() + L"'s game mode to " + newMode->getName()); + } + } +} - // player.setGameMode(newMode); - // player.fallDistance = 0; // reset falldistance so flying people do not die :P - - // ChatMessageComponent mode = ChatMessageComponent.forTranslation("gameMode." + newMode.getName()); - - // if (player != source) { - // logAdminAction(source, AdminLogCommand.LOGTYPE_DONT_SHOW_TO_SELF, "commands.gamemode.success.other", player.getAName(), mode); - // } else { - // logAdminAction(source, AdminLogCommand.LOGTYPE_DONT_SHOW_TO_SELF, "commands.gamemode.success.self", mode); - // } - - // return; - //} - - //throw new UsageException("commands.gamemode.usage"); +shared_ptr GameModeCommand::preparePacket(shared_ptr player, int gameMode) +{ + if (player == nullptr) return nullptr; + ByteArrayOutputStream baos; + DataOutputStream dos(&baos); + dos.writePlayerUID(player->getXuid()); + dos.writeInt(gameMode); + return std::make_shared(eGameCommand_GameMode, baos.toByteArray()); } GameType *GameModeCommand::getModeForString(shared_ptr source, const wstring &name) { - return nullptr; - //if (name.equalsIgnoreCase(GameType.SURVIVAL.getName()) || name.equalsIgnoreCase("s")) { - // return GameType.SURVIVAL; - //} else if (name.equalsIgnoreCase(GameType.CREATIVE.getName()) || name.equalsIgnoreCase("c")) { - // return GameType.CREATIVE; - //} else if (name.equalsIgnoreCase(GameType.ADVENTURE.getName()) || name.equalsIgnoreCase("a")) { - // return GameType.ADVENTURE; - //} else { - // return LevelSettings.validateGameType(convertArgToInt(source, name, 0, GameType.values().length - 2)); - //} + return GameType::byName(name); } \ No newline at end of file diff --git a/Minecraft.World/GameModeCommand.h b/Minecraft.World/GameModeCommand.h index 302be9f5..70dd10af 100644 --- a/Minecraft.World/GameModeCommand.h +++ b/Minecraft.World/GameModeCommand.h @@ -1,8 +1,8 @@ #pragma once #include "Command.h" - class GameType; +class GameCommandPacket; class GameModeCommand : public Command { @@ -10,6 +10,7 @@ public: virtual EGameCommand getId(); int getPermissionLevel(); virtual void execute(shared_ptr source, byteArray commandData); + static shared_ptr preparePacket(shared_ptr player, int gameMode); protected: GameType *getModeForString(shared_ptr source, const wstring &name); diff --git a/Minecraft.World/Guardian.cpp b/Minecraft.World/Guardian.cpp index dd0de42e..59cd023a 100644 --- a/Minecraft.World/Guardian.cpp +++ b/Minecraft.World/Guardian.cpp @@ -202,12 +202,7 @@ float Guardian::getSpikesAnimation(float partialTicks) -int Guardian::getAmbientSound() { return -1; } -int Guardian::getHurtSound() { return -1; } -int Guardian::getDeathSound() { return -1; } -float Guardian::getSoundVolume() { return 1.0f; } -int Guardian::getDeathLoot() { return 0; } -bool Guardian::makeStepSound() { return false; } + void Guardian::dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel) { @@ -261,31 +256,36 @@ void Guardian::serverAiStep() { WaterAnimal::serverAiStep(); - if (isElder() && !level->isClientSide) + if (isElder() && !level->isClientSide) +{ + bool periodicTick = ((tickCount + entityId) % 1200 == 0); + + auto& players = level->players; + for (int i = 0; i < (int)players.size(); i++) { - if (((tickCount + entityId) % 1200 == 0)) + shared_ptr player = players[i]; + if (!player) continue; + if (distanceToSqr(player) >= 2500.0) continue; + if (player->abilities.invulnerable) continue; + + MobEffectInstance* existing = player->getEffect(MobEffect::digSlowdown); + bool noEffect = (existing == nullptr); + bool weakEffect = (existing != nullptr && existing->getAmplifier() < 2); + bool shortEffect= (existing != nullptr && existing->getDuration() < 1200); + + + bool applyNow = noEffect || weakEffect || (periodicTick && shortEffect); + + if (applyNow) { - auto& players = level->players; - for (int i = 0; i < (int)players.size(); i++) - { - shared_ptr player = players[i]; - if (!player) continue; + player->addEffect(new MobEffectInstance(MobEffect::digSlowdown->id, 6000, 2)); - if (distanceToSqr(player) >= 2500.0) - continue; - if (player->abilities.invulnerable) - continue; - - - MobEffectInstance *existing = player->getEffect(MobEffect::digSlowdown); - if (existing == nullptr || existing->getAmplifier() < 2 || existing->getDuration() < 1200) - { - player->addEffect(new MobEffectInstance(MobEffect::digSlowdown->id, 6000, 2)); - } - } + level->broadcastEntityEvent(shared_from_this(), (byte)8); + playCurseSound(); } } +} if (!hasTargetedEntity()) @@ -320,6 +320,7 @@ void Guardian::serverAiStep() if (attackTimer == 1) { level->broadcastEntityEvent(shared_from_this(), 21); + playAttackSound(); } else if (attackTimer >= getAttackDuration()) { @@ -379,6 +380,15 @@ void Guardian::aiStep() updateSize(isElder()); clientSideTailAnimationO = clientSideTailAnimation; + if (!isInWater() && onGround) + { + if (clientSideTouchedGround) + { + playFlopSound(); + clientSideTouchedGround = false; + } + } + if (!isInWater()) { clientSideTailAnimation = 2.0f; @@ -478,4 +488,140 @@ MobGroupData *Guardian::finalizeMobSpawn(MobGroupData *groupData, int extraData) if (extraData == 1) setElder(true); return groupData; +} + +void Guardian::handleEntityEvent(byte eventId) +{ + + if (eventId == 8 && level->isClientSide && isElder()) + { + + shared_ptr localPlayer = level->getNearestPlayer(x, y, z, 64.0); + if (localPlayer != nullptr) + { + level->addParticle( + eParticleType_mobAppearance, + localPlayer->x, + localPlayer->bb->y0, + localPlayer->z, + 0.0, 0.0, 0.0); + } + } + + WaterAnimal::handleEntityEvent(eventId); +} + +int Guardian::getAmbientSound() +{ + if (!isInWater()) + { + if (isElder()) + { + + return eSoundType_MOB_ELDER_GUARDIAN_IDLE; + + } + else + { + + return eSoundType_MOB_GUARDIAN_LAND_IDLE; + + + } + } + return -1; +} + +int Guardian::getHurtSound() +{ + if (!isInWater()) + { + if (isElder()) + { + + return eSoundType_MOB_ELDER_GUARDIAN_HIT; + + + } + else + { + + + return eSoundType_MOB_GUARDIAN_LAND_HIT; + + + } + } + else + { + if (isElder()) + { + + + + return eSoundType_MOB_ELDER_GUARDIAN_HIT; + + } + else + { + + return eSoundType_MOB_GUARDIAN_HIT; + + } + } + +} + +int Guardian::getDeathSound() +{ + if (!isInWater()) + { + if (isElder()) + return eSoundType_MOB_ELDER_GUARDIAN_DEATH; + else + return eSoundType_MOB_GUARDIAN_LAND_DEATH; + } + else + { + if (isElder()) + return eSoundType_MOB_ELDER_GUARDIAN_DEATH; + else + return eSoundType_MOB_GUARDIAN_DEATH; + } +} + +bool Guardian::makeStepSound() +{ + return false; +} + + +void Guardian::playFlopSound() +{ + + level->playSound(x, y, z, eSoundType_MOB_GUARDIAN_FLOP, getSoundVolume(), 1.0f); + +} + + +void Guardian::playAttackSound() +{ + + level->playSound(x, y, z, eSoundType_MOB_GUARDIAN_ATTACK_LOOP, getSoundVolume(), 1.0f); +} + +void Guardian::playCurseSound() +{ + + level->playSound(x, y, z, eSoundType_MOB_ELDER_GUARDIAN_CURSE, getSoundVolume(), 1.0f); +} + +float Guardian::getSoundVolume() +{ + return 1.0f; +} + +int Guardian::getDeathLoot() +{ + return 0; } \ No newline at end of file diff --git a/Minecraft.World/Guardian.h b/Minecraft.World/Guardian.h index b03bf7bd..826ec376 100644 --- a/Minecraft.World/Guardian.h +++ b/Minecraft.World/Guardian.h @@ -36,6 +36,9 @@ protected: virtual float getSoundVolume(); virtual int getDeathLoot(); virtual bool makeStepSound(); + void playFlopSound(); + void playAttackSound(); + void playCurseSound(); virtual void dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel); virtual void serverAiStep(); @@ -66,6 +69,7 @@ public: float getTailAnimation(float partialTicks); float getSpikesAnimation(float partialTicks); float getAttackAnimationScale(float partialTicks); + virtual void handleEntityEvent(byte eventId) override; protected: void lookAt(shared_ptr e, float yMax, float xMax); @@ -76,4 +80,5 @@ private: float tx, ty, tz; int attackTimer; shared_ptr targetedEntity; + }; \ No newline at end of file diff --git a/Minecraft.World/ParticleTypes.h b/Minecraft.World/ParticleTypes.h index 646bfdd8..130d5cdb 100644 --- a/Minecraft.World/ParticleTypes.h +++ b/Minecraft.World/ParticleTypes.h @@ -41,6 +41,7 @@ enum ePARTICLE_TYPE eParticleType_angryVillager, eParticleType_happyVillager, eParticleType_fireworksspark, + eParticleType_mobAppearance, // 4J-JEV: In the java, the particle name was used to sneak parameters in for the Terrain and IconCrack particle constructors. diff --git a/Minecraft.World/SoundTypes.h b/Minecraft.World/SoundTypes.h index 971d4661..a2d1bf4d 100644 --- a/Minecraft.World/SoundTypes.h +++ b/Minecraft.World/SoundTypes.h @@ -263,7 +263,19 @@ enum eSOUND_TYPE eSoundType_DAMAGE_CRITICAL, eSoundType_ITEM_ELYTRA_FLYING, - + + eSoundType_MOB_GUARDIAN_ATTACK_LOOP, + eSoundType_MOB_GUARDIAN_DEATH, + eSoundType_MOB_GUARDIAN_HIT, + eSoundType_MOB_GUARDIAN_FLOP, + eSoundType_MOB_GUARDIAN_LAND_DEATH, + eSoundType_MOB_GUARDIAN_LAND_HIT, + eSoundType_MOB_GUARDIAN_LAND_IDLE, + eSoundType_MOB_ELDER_GUARDIAN_CURSE, + eSoundType_MOB_ELDER_GUARDIAN_DEATH, + eSoundType_MOB_ELDER_GUARDIAN_HIT, + eSoundType_MOB_ELDER_GUARDIAN_IDLE, + eSoundType_MAX }; diff --git a/Minecraft.World/TimeCommand.cpp b/Minecraft.World/TimeCommand.cpp index 0e4766cb..af9a4eba 100644 --- a/Minecraft.World/TimeCommand.cpp +++ b/Minecraft.World/TimeCommand.cpp @@ -81,5 +81,15 @@ shared_ptr TimeCommand::preparePacket(bool night) dos.writeBoolean(night); + return std::make_shared(eGameCommand_Time, baos.toByteArray()); +} + +shared_ptr TimeCommand::preparePacket(int ticks) +{ + ByteArrayOutputStream baos; + DataOutputStream dos(&baos); + + dos.writeInt(ticks); + return std::make_shared(eGameCommand_Time, baos.toByteArray()); } \ No newline at end of file diff --git a/Minecraft.World/TimeCommand.h b/Minecraft.World/TimeCommand.h index 20d4ef54..254aa0fe 100644 --- a/Minecraft.World/TimeCommand.h +++ b/Minecraft.World/TimeCommand.h @@ -15,4 +15,5 @@ protected: public: static shared_ptr preparePacket(bool night); + static shared_ptr preparePacket(int ticks); }; \ No newline at end of file diff --git a/NOTES.md b/NOTES.md new file mode 100644 index 00000000..33d64cdf --- /dev/null +++ b/NOTES.md @@ -0,0 +1,17 @@ +![Banner](https://github.com/pieeebot/neoLegacy/raw/main/.github/banner.png) + +# neoLegacy v1.0.1b + +- Classic Crafting +- Commands support! + - /give + - /tp - /teleport + - /gamemode + - .... + +image + +# 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 6e4e721d..c6124e69 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ # neoLegacy (Legacy Console Edition) This project aims to backport the newer title updates back to Legacy Console Edition (which is based on TU19). -[![Discord](https://img.shields.io/badge/Discord-Join%20Server-5865F2?logo=discord&logoColor=white)](https://discord.gg/D6hEPNYeyn) +[![Discord](https://img.shields.io/badge/Discord-Join%20Server-5865F2?logo=discord&logoColor=white)](https://discord.gg/neolegacy) [![Emerald Launcher](https://img.shields.io/badge/Get_it_on-LCE_Emerald_Launcher-3fc724)](https://github.com/LCE-Hub/LCE-Emerald-Launcher) # Our roadmap: diff --git a/cmake/GenerateItemNameMap.cmake b/cmake/GenerateItemNameMap.cmake new file mode 100644 index 00000000..88f596aa --- /dev/null +++ b/cmake/GenerateItemNameMap.cmake @@ -0,0 +1,73 @@ +if(NOT INPUT_FILES) + message(FATAL_ERROR "INPUT_FILES must be set.") +endif() + +if(NOT OUTPUT_FILE) + message(FATAL_ERROR "OUTPUT_FILE must be set.") +endif() + +set(_entries "") + +foreach(_file IN LISTS INPUT_FILES) + if(NOT EXISTS "${_file}") + message(FATAL_ERROR "Input file does not exist: ${_file}") + endif() + + file(READ "${_file}" _raw) + string(REPLACE "\r\n" "\n" _raw "${_raw}") + string(REPLACE "\r" "\n" _raw "${_raw}") + string(REPLACE "\n" ";" _lines "${_raw}") + + foreach(_line IN LISTS _lines) + if(_line MATCHES "static const int ([A-Za-z_][A-Za-z0-9_]*_Id)[ \t]*=[ \t]*([0-9]+)") + set(_var "${CMAKE_MATCH_1}") + set(_id "${CMAKE_MATCH_2}") + string(REGEX REPLACE "_Id$" "" _name "${_var}") + if(_entries) + string(APPEND _entries ",\n { \"${_name}\", ${_id} }") + else() + set(_entries " { \"${_name}\", ${_id} }") + endif() + endif() + endforeach() +endforeach() + +set(_tmp "${OUTPUT_FILE}.tmp") +file(WRITE "${_tmp}" +"#pragma once\n" +"\n" +"#include \n" +"#include \n" +"\n" +"inline const std::unordered_map g_ItemNameMap =\n" +"{\n" +"${_entries}\n" +"};\n" +"\n" +"inline int GetItemIdByName(const std::string& name)\n" +"{\n" +" auto it = g_ItemNameMap.find(name);\n" +" return (it != g_ItemNameMap.end()) ? it->second : -1;\n" +"}\n" +"inline int GetItemIdByName(const std::wstring& name)\n" +"{\n" +" return GetItemIdByName(std::string(name.begin(), name.end()));\n" +"}\n" +) + +if(EXISTS "${OUTPUT_FILE}") + execute_process( + COMMAND ${CMAKE_COMMAND} -E compare_files "${OUTPUT_FILE}" "${_tmp}" + RESULT_VARIABLE _changed + ) +else() + set(_changed 1) +endif() + +if(_changed) + file(RENAME "${_tmp}" "${OUTPUT_FILE}") + message(STATUS "GenerateItemNameMap: wrote ${OUTPUT_FILE}") +else() + file(REMOVE "${_tmp}") + message(STATUS "GenerateItemNameMap: ${OUTPUT_FILE} is up-to-date") +endif() \ No newline at end of file