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/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/LevelRenderer.cpp b/Minecraft.Client/LevelRenderer.cpp index de422457..7ea382a2 100644 --- a/Minecraft.Client/LevelRenderer.cpp +++ b/Minecraft.Client/LevelRenderer.cpp @@ -141,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; @@ -171,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()); @@ -261,6 +262,7 @@ LevelRenderer::LevelRenderer(Minecraft *mc, Textures *textures) t->end(); glEndList(); } +#endif Chunk::levelRenderer = this; @@ -536,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; @@ -662,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() @@ -3960,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_ptr + #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/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.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.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/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/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