MorriCraft v2.3.0: Accurate download progress bar based on file size, Major Update release

This commit is contained in:
Michael Howard 2026-04-25 14:12:49 -05:00
parent a16868c8b1
commit d8781b0211
14 changed files with 70 additions and 31 deletions

View File

@ -106,7 +106,7 @@ A pre-built `MorriCraft-Windows.zip` is available in the repository root.
## 📜 Version History ## 📜 Version History
### v2.2.29 - Hunger & World Gen Final (Current) ### v2.3.0 - Major Update: Hunger System & Accurate Updater (Current)
- **Hunger System**: Added player hunger bar, starvation mechanics, and health regeneration when full. - **Hunger System**: Added player hunger bar, starvation mechanics, and health regeneration when full.
- **Apple Item**: Added edible Apple item to restore hunger. - **Apple Item**: Added edible Apple item to restore hunger.
- **Grass Texture**: Improved `grass_top` texture with better pixel art. - **Grass Texture**: Improved `grass_top` texture with better pixel art.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 7.0 KiB

View File

@ -1 +1 @@
v2.2.29 v2.3.0

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 7.0 KiB

View File

@ -1 +1 @@
v2.2.29 v2.3.0

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 7.0 KiB

View File

@ -1 +1 @@
v2.2.29 v2.3.0

Binary file not shown.

Binary file not shown.

View File

@ -1 +1 @@
v2.2.29 v2.3.0

View File

@ -386,7 +386,7 @@ void RebuildChunkRenderList(Chunk* chunk, int cx, int cz) {
unsigned char faces = GetExposedFaces(lx, ly, lz, chunk, nxM, nxP, nzM, nzP); unsigned char faces = GetExposedFaces(lx, ly, lz, chunk, nxM, nxP, nzM, nzP);
if (faces != 0) { if (faces != 0) {
// Simple Torch Lighting (v2.2.29) // Simple Torch Lighting (v2.3.0)
float blockLight = 0.0f; float blockLight = 0.0f;
Vector3 bPos = {(float)(worldX+lx), (float)ly, (float)(worldZ+lz)}; Vector3 bPos = {(float)(worldX+lx), (float)ly, (float)(worldZ+lz)};
for (const auto& tp : torchPositions) { for (const auto& tp : torchPositions) {
@ -511,6 +511,29 @@ std::string GetRemoteVersion() {
return result; return result;
} }
long GetRemoteFileSize(std::string url) {
#ifdef _WIN32
std::string cmd = "powershell -Command \"[long](Invoke-WebRequest -Method Head -Uri '" + url + "').Headers.'Content-Length'\"";
#else
std::string cmd = "curl -sI -L \"" + url + "\" | grep -i Content-Length | tail -n 1 | awk '{print $2}' | tr -d '\\r'";
#endif
FILE* pipe = popen(cmd.c_str(), "r");
if (!pipe) return 0;
char buffer[128];
long size = 0;
if (fgets(buffer, sizeof(buffer), pipe) != NULL) {
size = std::atol(buffer);
}
pclose(pipe);
return size;
}
long GetLocalFileSize(std::string filename) {
std::ifstream in(filename, std::ifstream::ate | std::ifstream::binary);
if (!in.is_open()) return 0;
return (long)in.tellg();
}
bool IsVersionNewer(std::string remote, std::string local) { bool IsVersionNewer(std::string remote, std::string local) {
if (remote.empty() || remote == "error" || remote == local) return false; if (remote.empty() || remote == "error" || remote == local) return false;
int r1=0, r2=0, r3=0, l1=0, l2=0, l3=0; int r1=0, r2=0, r3=0, l1=0, l2=0, l3=0;
@ -673,7 +696,7 @@ float FindSpawnY(int spawnX, int spawnZ) {
// Feet land at top of block (y + 0.5). // Feet land at top of block (y + 0.5).
// We use +0.6f + 1.6f to ensure we start slightly ABOVE the block // We use +0.6f + 1.6f to ensure we start slightly ABOVE the block
// to avoid getting stuck in collision on frame 1. // to avoid getting stuck in collision on frame 1.
// v2.2.29: Spawn higher (feet at y + 1.5, eyes at y + 3.1) to avoid ground-clipping // v2.3.0: Spawn higher (feet at y + 1.5, eyes at y + 3.1) to avoid ground-clipping
return (float)y + 1.5f + 1.6f; return (float)y + 1.5f + 1.6f;
} }
} }
@ -714,7 +737,7 @@ void GenerateChunk(int cx, int cz) {
// Combine: broad hills (±12) + local detail (±4) = up to ±16 around Y=32 // Combine: broad hills (±12) + local detail (±4) = up to ±16 around Y=32
int height = 32 + (int)(continentNoise * 12.0f) + (int)(detailNoise * 4.0f); int height = 32 + (int)(continentNoise * 12.0f) + (int)(detailNoise * 4.0f);
// Spawn Plateau (v2.2.29): Smoothly flatten area around (0,0) // Spawn Plateau (v2.3.0): Smoothly flatten area around (0,0)
float distToSpawn = sqrtf(worldX * worldX + worldZ * worldZ); float distToSpawn = sqrtf(worldX * worldX + worldZ * worldZ);
if (distToSpawn < 12.0f) { if (distToSpawn < 12.0f) {
float plateauStrength = 1.0f - (distToSpawn / 12.0f); float plateauStrength = 1.0f - (distToSpawn / 12.0f);
@ -726,7 +749,7 @@ void GenerateChunk(int cx, int cz) {
if (height < 10) height = 10; if (height < 10) height = 10;
if (height >= CHUNK_HEIGHT - 2) height = CHUNK_HEIGHT - 2; if (height >= CHUNK_HEIGHT - 2) height = CHUNK_HEIGHT - 2;
// Biome noise: determines surface type (v2.2.29: 4x larger biomes) // Biome noise: determines surface type (v2.3.0: 4x larger biomes)
float biomeNoise = fbm(worldX * 0.002f, worldZ * 0.002f, globalSeedHash + 77777); float biomeNoise = fbm(worldX * 0.002f, worldZ * 0.002f, globalSeedHash + 77777);
for (int y = 0; y <= height; y++) { for (int y = 0; y <= height; y++) {
@ -1011,7 +1034,7 @@ int main(void)
// By default, windows have minimize, maximize, and close buttons on the top bar. // By default, windows have minimize, maximize, and close buttons on the top bar.
SetConfigFlags(FLAG_WINDOW_RESIZABLE | FLAG_VSYNC_HINT); SetConfigFlags(FLAG_WINDOW_RESIZABLE | FLAG_VSYNC_HINT);
InitWindow(screenWidth, screenHeight, "MorriCraft v2.2.29"); InitWindow(screenWidth, screenHeight, "MorriCraft v2.3.0");
LoadConfig(); LoadConfig();
SetExitKey(KEY_NULL); // Prevent ESC from closing the window SetExitKey(KEY_NULL); // Prevent ESC from closing the window
@ -1081,7 +1104,7 @@ int main(void)
float updateTimer = 0.0f; float updateTimer = 0.0f;
float downloadProgress = 0.0f; float downloadProgress = 0.0f;
std::string latestVersion = ""; std::string latestVersion = "";
std::string localVersion = "v2.2.29"; // Default fallback std::string localVersion = "v2.3.0"; // Default fallback
// Read local version // Read local version
std::ifstream vfile("assets/version.txt"); std::ifstream vfile("assets/version.txt");
@ -1693,7 +1716,7 @@ int main(void)
// --- GLOBAL TIME & AUDIO MANAGEMENT --- // --- GLOBAL TIME & AUDIO MANAGEMENT ---
float cycleLength = 600.0f; // Slower day cycle (v2.2.29: cut speed in half) float cycleLength = 600.0f; // Slower day cycle (v2.3.0: cut speed in half)
if (currentState == GAMEPLAY) { if (currentState == GAMEPLAY) {
gameTime += GetFrameTime(); gameTime += GetFrameTime();
@ -2079,7 +2102,7 @@ int main(void)
AddToInventory(targetBlock); AddToInventory(targetBlock);
NetSetBlock(hitX, hitY, hitZ, AIR); NetSetBlock(hitX, hitY, hitZ, AIR);
// Tool Durability Logic (v2.2.29) // Tool Durability Logic (v2.3.0)
InventorySlot* slot = &hotbar[activeHotbarSlot]; InventorySlot* slot = &hotbar[activeHotbarSlot];
if (slot->maxDurability > 0) { if (slot->maxDurability > 0) {
slot->durability--; slot->durability--;
@ -2242,7 +2265,7 @@ int main(void)
} else if (currentState == DOWNLOADING_UPDATE) { } else if (currentState == DOWNLOADING_UPDATE) {
if (!startedDownload) { if (!startedDownload) {
startedDownload = true; startedDownload = true;
currentProgress = 0.1f; currentProgress = 0.0f;
std::thread([&]() { std::thread([&]() {
std::string binaryUrl, binaryName; std::string binaryUrl, binaryName;
#ifdef _WIN32 #ifdef _WIN32
@ -2254,15 +2277,31 @@ int main(void)
#endif #endif
std::string versionUrl = "https://git.linology.tech/michael/MorriCraft/raw/branch/master/release/version.txt"; std::string versionUrl = "https://git.linology.tech/michael/MorriCraft/raw/branch/master/release/version.txt";
// Download binary long remoteSize = GetRemoteFileSize(binaryUrl);
int r1 = system(("curl -L -s -o " + binaryName + " \"" + binaryUrl + "\"").c_str()); bool binaryDone = false;
currentProgress = 0.85f; // Most of the way done after binary
// Start download in background
std::thread([&, binaryName, binaryUrl, &binaryDone]() {
system(("curl -L -s -o " + binaryName + " \"" + binaryUrl + "\"").c_str());
binaryDone = true;
}).detach();
// Monitor progress
while (!binaryDone) {
if (remoteSize > 0) {
long localSize = GetLocalFileSize(binaryName);
currentProgress = (float)localSize / (float)remoteSize * 0.95f;
}
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
currentProgress = 0.96f;
// Download version // Download version
int r2 = system(("curl -L -s -o version.txt.new \"" + versionUrl + "\"").c_str()); int r2 = system(("curl -L -s -o version.txt.new \"" + versionUrl + "\"").c_str());
currentProgress = 1.0f; currentProgress = 1.0f;
if (r1 == 0 && r2 == 0) { if (r2 == 0) {
// Switch to extraction phase // Switch to extraction phase
currentState = EXTRACTING_UPDATE; currentState = EXTRACTING_UPDATE;
isInstalling = true; isInstalling = true;
@ -2602,8 +2641,8 @@ int main(void)
DrawTexturePro(titleTexture, sourceRec, destRec, origin, 0.0f, WHITE); DrawTexturePro(titleTexture, sourceRec, destRec, origin, 0.0f, WHITE);
EndMode2D(); EndMode2D();
// Show Version Number (v2.2.29) in Red // Show Version Number (v2.3.0) in Red
DrawTextEx(customFont, "v2.2.29", (Vector2){ 20, (float)currentHeight - 30 }, 22, 1.0f, RED); DrawTextEx(customFont, "v2.3.0", (Vector2){ 20, (float)currentHeight - 30 }, 22, 1.0f, RED);
// --- PLAYER NAME POPUP (IF MISSING) --- // --- PLAYER NAME POPUP (IF MISSING) ---
if (playerName == "") { if (playerName == "") {
@ -2781,7 +2820,7 @@ int main(void)
spawnSavedX = 0; spawnSavedY = -1; spawnSavedZ = 0; spawnSavedX = 0; spawnSavedY = -1; spawnSavedZ = 0;
} }
// Load Chests (v2.2.29) // Load Chests (v2.3.0)
chestInventories.clear(); chestInventories.clear();
std::ifstream cf("saves/" + currentWorldName + "/chests.dat", std::ios::binary); std::ifstream cf("saves/" + currentWorldName + "/chests.dat", std::ios::binary);
if (cf.is_open()) { if (cf.is_open()) {
@ -2799,7 +2838,7 @@ int main(void)
cf.close(); cf.close();
} }
// Load Torches (v2.2.29) // Load Torches (v2.3.0)
torchPositions.clear(); torchPositions.clear();
std::ifstream tf("saves/" + currentWorldName + "/torches.dat", std::ios::binary); std::ifstream tf("saves/" + currentWorldName + "/torches.dat", std::ios::binary);
if (tf.is_open()) { if (tf.is_open()) {
@ -3083,7 +3122,7 @@ int main(void)
snprintf(worldName, sizeof(worldName), "%s", currentWorldName.c_str()); snprintf(worldName, sizeof(worldName), "%s", currentWorldName.c_str());
worldNameLen = strlen(worldName); worldNameLen = strlen(worldName);
gameTime = 150.0f; // Start new world at 7:00 AM (v2.2.29 adjusted for slower cycle) gameTime = 150.0f; // Start new world at 7:00 AM (v2.3.0 adjusted for slower cycle)
if (serverMode) { if (serverMode) {
serverSocket = socket(AF_INET, SOCK_STREAM, 0); serverSocket = socket(AF_INET, SOCK_STREAM, 0);
@ -3237,7 +3276,7 @@ int main(void)
} else if (renderType == TORCH) { } else if (renderType == TORCH) {
for (Chunk* chunk : visibleChunks) { for (Chunk* chunk : visibleChunks) {
for (auto& data : chunk->renderLists[TORCH]) { for (auto& data : chunk->renderLists[TORCH]) {
// 3D Flickering Torch (v2.2.29) // 3D Flickering Torch (v2.3.0)
float flicker = sinf(GetTime() * 12.0f) * 0.03f; float flicker = sinf(GetTime() * 12.0f) * 0.03f;
float h = 0.6f + flicker; float h = 0.6f + flicker;
float w = 0.12f; float w = 0.12f;
@ -3506,7 +3545,7 @@ int main(void)
(Vector2){ (float)(sx + (slotSize/2) - (countSize.x/2)), (float)(hotbarY + slotSize - 14) }, (Vector2){ (float)(sx + (slotSize/2) - (countSize.x/2)), (float)(hotbarY + slotSize - 14) },
12, 1.0f, WHITE); 12, 1.0f, WHITE);
// Draw Hotbar Durability Bar (v2.2.29) // Draw Hotbar Durability Bar (v2.3.0)
if (hotbar[s].maxDurability > 0 && hotbar[s].durability < hotbar[s].maxDurability) { if (hotbar[s].maxDurability > 0 && hotbar[s].durability < hotbar[s].maxDurability) {
float durP = (float)hotbar[s].durability / hotbar[s].maxDurability; float durP = (float)hotbar[s].durability / hotbar[s].maxDurability;
int barW = slotSize - 10; int barW = slotSize - 10;
@ -3537,7 +3576,7 @@ int main(void)
DrawRectangleRec(slotRect, hov ? (Color){90,90,90,255} : (Color){60,60,60,255}); DrawRectangleRec(slotRect, hov ? (Color){90,90,90,255} : (Color){60,60,60,255});
DrawRectangleLinesEx(slotRect, 2.0f, hov ? WHITE : (Color){100,100,100,200}); DrawRectangleLinesEx(slotRect, 2.0f, hov ? WHITE : (Color){100,100,100,200});
// Draw Durability Bar (v2.2.29) // Draw Durability Bar (v2.3.0)
if (slot.maxDurability > 0 && slot.durability < slot.maxDurability) { if (slot.maxDurability > 0 && slot.durability < slot.maxDurability) {
float durPercent = (float)slot.durability / slot.maxDurability; float durPercent = (float)slot.durability / slot.maxDurability;
int barW = slotRect.width - 10; int barW = slotRect.width - 10;
@ -3814,7 +3853,7 @@ int main(void)
} }
} }
if (hov && IsMouseButtonPressed(MOUSE_RIGHT_BUTTON)) { if (hov && IsMouseButtonPressed(MOUSE_RIGHT_BUTTON)) {
// Right click: pick up/place one (v2.2.29) // Right click: pick up/place one (v2.3.0)
if (mouseHeldItem.blockType == AIR) { if (mouseHeldItem.blockType == AIR) {
if (chestInv[i].blockType != AIR && chestInv[i].count > 0) { if (chestInv[i].blockType != AIR && chestInv[i].count > 0) {
mouseHeldItem = InventorySlot(chestInv[i].blockType, 1); mouseHeldItem = InventorySlot(chestInv[i].blockType, 1);
@ -3861,7 +3900,7 @@ int main(void)
} }
} }
if (hov && IsMouseButtonPressed(MOUSE_RIGHT_BUTTON)) { if (hov && IsMouseButtonPressed(MOUSE_RIGHT_BUTTON)) {
// Right click logic for player inv in chest GUI (v2.2.29) // Right click logic for player inv in chest GUI (v2.3.0)
if (mouseHeldItem.blockType == AIR) { if (mouseHeldItem.blockType == AIR) {
if (inventory[i].blockType != AIR && inventory[i].count > 0) { if (inventory[i].blockType != AIR && inventory[i].count > 0) {
mouseHeldItem = InventorySlot(inventory[i].blockType, 1); mouseHeldItem = InventorySlot(inventory[i].blockType, 1);
@ -4040,7 +4079,7 @@ int main(void)
invf.close(); invf.close();
} }
// Save Chests (v2.2.29) // Save Chests (v2.3.0)
std::ofstream cf("saves/" + currentWorldName + "/chests.dat", std::ios::binary); std::ofstream cf("saves/" + currentWorldName + "/chests.dat", std::ios::binary);
if (cf.is_open()) { if (cf.is_open()) {
uint32_t count = (uint32_t)chestInventories.size(); uint32_t count = (uint32_t)chestInventories.size();
@ -4056,7 +4095,7 @@ int main(void)
cf.close(); cf.close();
} }
// Save Torches (v2.2.29) // Save Torches (v2.3.0)
std::ofstream tf("saves/" + currentWorldName + "/torches.dat", std::ios::binary); std::ofstream tf("saves/" + currentWorldName + "/torches.dat", std::ios::binary);
if (tf.is_open()) { if (tf.is_open()) {
uint32_t count = (uint32_t)torchPositions.size(); uint32_t count = (uint32_t)torchPositions.size();

View File

@ -1 +1 @@
v2.2.29 v2.3.0