From 67b50beb90c7187492a3b824ddd4fdb02fd302df Mon Sep 17 00:00:00 2001 From: p2r3 Date: Fri, 29 Aug 2025 06:22:05 +0300 Subject: [PATCH] support syncing to flash on esp-idf via littlefs --- include/globals.h | 9 ++++++++- include/serialize.h | 2 +- src/serialize.c | 45 ++++++++++++++++++++++++++++++++++++++------- 3 files changed, 47 insertions(+), 9 deletions(-) diff --git a/include/globals.h b/include/globals.h index 25d4b45..c12e8cd 100644 --- a/include/globals.h +++ b/include/globals.h @@ -38,11 +38,18 @@ // How many player-made block changes to allow // Determines the fixed amount of memory allocated to blocks #define MAX_BLOCK_CHANGES 20000 -// If defined, writes and reads world data to/from disk (PC only). +// If defined, writes and reads world data to/from disk (or flash). // This is a synchronous operation, and can cause performance issues if // frequent random disk access is slow. Data is still stored in and // accessed from memory - reading from disk is only done on startup. +// When targeting ESP-IDF, LittleFS is used to manage flash reads and +// writes. Consider increasing DISK_SYNC_INTERVAL if wear is a concern. #define SYNC_WORLD_TO_DISK +// The minimum interval (in microseconds) at which certain data is written +// to disk/flash. Bounded on the low end by TIME_BETWEEN_TICKS. Currently +// only applies to player data. Block changes are written as soon as they +// are made, but in much smaller portions. +#define DISK_SYNC_INTERVAL 15000000 // If defined, scales the frequency at which player movement updates are // broadcast based on the amount of players, reducing overhead for higher // player counts. For very many players, makes movement look jittery. diff --git a/include/serialize.h b/include/serialize.h index 2c3cf71..eeffb46 100644 --- a/include/serialize.h +++ b/include/serialize.h @@ -3,7 +3,7 @@ #include "globals.h" -#if defined(SYNC_WORLD_TO_DISK) && !defined(ESP_PLATFORM) +#ifdef SYNC_WORLD_TO_DISK int initSerializer (); void writeBlockChangesToDisk (int from, int to); void writeChestChangesToDisk (uint8_t *storage_ptr, uint8_t slot); diff --git a/src/serialize.c b/src/serialize.c index 627947d..422fb0c 100644 --- a/src/serialize.c +++ b/src/serialize.c @@ -1,18 +1,44 @@ #include "globals.h" -#if defined(SYNC_WORLD_TO_DISK) && !defined(ESP_PLATFORM) +#ifdef SYNC_WORLD_TO_DISK -#include -#include +#ifdef ESP_PLATFORM + #include "esp_littlefs.h" + #define FILE_PATH "/littlefs/world.bin" +#else + #include + #define FILE_PATH "world.bin" +#endif +#include "tools.h" #include "registries.h" #include "serialize.h" +int64_t last_disk_sync_time = 0; + // Restores world data from disk, or writes world file if it doesn't exist int initSerializer () { + last_disk_sync_time = get_program_time(); + + #ifdef ESP_PLATFORM + esp_vfs_littlefs_conf_t conf = { + .base_path = "/littlefs", + .partition_label = "littlefs", + .format_if_mount_failed = true, + .dont_mount = false + }; + + esp_err_t ret = esp_vfs_littlefs_register(&conf); + if (ret != ESP_OK) { + printf("LittleFS error %d\n", ret); + perror("Failed to mount LittleFS. Aborting."); + return 1; + } + #endif + // Attempt to open existing world file - FILE *file = fopen("world.bin", "rb"); + FILE *file = fopen(FILE_PATH, "rb"); if (file) { // Read block changes from the start of the file directly into memory @@ -44,7 +70,7 @@ int initSerializer () { printf("No \"world.bin\" file found, creating one...\n\n"); // Try to create the file in binary write mode - file = fopen("world.bin", "wb"); + file = fopen(FILE_PATH, "wb"); if (!file) { perror( "Failed to open \"world.bin\" for writing.\n" @@ -83,13 +109,14 @@ int initSerializer () { } + return 0; } // Writes a range of block change entries to disk void writeBlockChangesToDisk (int from, int to) { // Try to open the file in rw (without overwriting) - FILE *file = fopen("world.bin", "r+b"); + FILE *file = fopen(FILE_PATH, "r+b"); if (!file) { perror("Failed to open \"world.bin\". Block updates have been dropped."); return; @@ -116,8 +143,12 @@ void writeBlockChangesToDisk (int from, int to) { // Writes all player data to disk void writePlayerDataToDisk () { + // Skip this write if enough time hasn't passed since the last one + if (get_program_time() - last_disk_sync_time < DISK_SYNC_INTERVAL) return; + last_disk_sync_time = get_program_time(); + // Try to open the file in rw (without overwriting) - FILE *file = fopen("world.bin", "r+b"); + FILE *file = fopen(FILE_PATH, "r+b"); if (!file) { perror("Failed to open \"world.bin\". Player updates have been dropped."); return;