Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Filesystem] Path Manager Improvements #4557

Merged
merged 3 commits into from
Jan 6, 2025

Conversation

Akkadius
Copy link
Member

@Akkadius Akkadius commented Nov 26, 2024

Description

This surfaced from some issues Brainiac was running into when trying to set his server up a certain way and aims to resolve the following scenarios

  • Allow for absolute paths to be set by any config parameter
  • Make the path resolution logic more resilient to allowing multiple scenarios to exist. Absolute, relative and fallbacks.
  • Create logging directory if it does not exist

Brainiac wants to be able to set absolute paths for most things, our current limited relative-only path loading falls short of being able to do that.

"directories": {
 "maps": "D:\\eqemu-server\\maps",
 "quests": "D:\\eqemu-server\\quests",
 "plugins": "D:\\eqemu-server\\plugins",
 "lua_modules": "D:\\eqemu-server\\lua_modules",
 "patches": "D:\\eqemu-server",
 "opcodes": "D:\\eqemu-server",
 "shared_memory": "D:\\eqemu-server\\shared",
 "logs": "D:\\eqemu-server\\logs"
}

Current Loading Hierarchy

  • Check relative first (what we use for most things today)
  • Check absolute second
  • Check fallback dirs (eg. Maps, maps - case sensitivity on Linux)
  • Use the config value directly if none of the above checks find an existing directory

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)

Testing

Tested by setting logs to a custom absolute path directory

"directories": {
  "logs": "/tmp/eq-logs",
  "patches": "assets/patches/",
  "opcodes": "assets/opcodes/"
}

Paths being loaded

  Zone |    Info    | LoadPaths ---------------------------------------------------------------------- 
  Zone |    Info    | LoadPaths          server > [/home/eqemu/server] 
  Zone |    Info    | LoadPaths            logs > [/tmp/eq-logs] 
  Zone |    Info    | LoadPaths        lua mods > [mods] 
  Zone |    Info    | LoadPaths     lua_modules > [quests/lua_modules] 
  Zone |    Info    | LoadPaths            maps > [maps] 
  Zone |    Info    | LoadPaths         patches > [assets/patches] 
  Zone |    Info    | LoadPaths          opcode > [assets/opcodes] 
  Zone |    Info    | LoadPaths         plugins > [quests/plugins] 
  Zone |    Info    | LoadPaths          quests > [quests] 
  Zone |    Info    | LoadPaths   shared_memory > [shared] 
  Zone |    Info    | LoadPaths ---------------------------------------------------------------------- 

Creating log directory if does not exist

  Zone |    Info    | StartFileLogs Logs directory not found, creating [/tmp/eq-logs] 
  Zone |    Info    | StartFileLogs Starting File Log [/tmp/eq-logs/zone/zone_5448.log] 

Checklist

  • I have tested my changes
  • I have performed a self-review of my code. Ensuring variables, functions and methods are named in a human-readable way, comments are added only where naming of variables, functions and methods can't give enough context.
  • I own the changes of my code and take responsibility for the potential issues that occur

@Akkadius
Copy link
Member Author

Putting this in draft as we may make additional tweaks to this

@Akkadius Akkadius marked this pull request as draft November 26, 2024 05:52
Comment on lines 39 to 46
if (File::Exists(fs::path{m_server_path + "/" + dir}.string())) {
return fs::relative(fs::path{m_server_path + "/" + dir}).lexically_normal().string();
}

// absolute
if (File::Exists(fs::path{dir}.string())) {
return fs::absolute(fs::path{dir}).string();
}
Copy link

@brainiac brainiac Nov 29, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

using std::filesystem::path to join instead of string concatenation will handle both of these cases:

fs::path server_path = fs::path(m_server_path);
std::error_code ec;

if (fs::path dir_path = server_path / dir; fs::is_directory(dir_path, ec) {
    return fs::proximate(dir_path).lexically_normal().string()
}

this can also be applied to the loop below.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Love the suggestion to use native join syntax versus the string format. Done.

I do actually want to return two distinctly separate relative versus absolute forms because -

  • In log messages relative reads easier eg logs vs /home/eqemu/server/logs
  • If the folder is not in a sub-directory of the server folder we display absolute /tmp/eq-logs/

Comment on lines +537 to +540
if (!File::Exists(path.GetLogPath())) {
LogInfo("Logs directory not found, creating [{}]", path.GetLogPath());
File::Makedir(path.GetLogPath());
}
Copy link

@brainiac brainiac Nov 29, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This directory creation occurs too late, because path manager would have already passed on setting the directory (due to it not existing), so path.GetLogPath() will return empty string.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't actually the case, the path resolver returns the exact value you set in your config if none of the relative or absolute directory checks succeed.

See resolver (return dir)

auto resolve_path = [&](const std::string& dir, const std::vector<std::string>& fallback_dirs = {}) -> std::string {
	// relative
	if (File::Exists((fs::path{m_server_path} / dir).string())) {
		return fs::relative(fs::path{m_server_path} / dir).lexically_normal().string();
	}

	// absolute
	if (File::Exists(fs::path{dir}.string())) {
		return fs::absolute(fs::path{dir}).string();
	}

	// fallback search options if specified
	for (const auto& fallback : fallback_dirs) {
		if (File::Exists((fs::path{m_server_path} / fallback).string())) {
			return fs::relative(fs::path{m_server_path} / fallback).lexically_normal().string();
		}
	}

	// if all else fails, just set it to the config value
	return dir;
};

And then at this stage of the code the logging code that this is in will make sure that the path exists before it tries to write to it. In this case the path didn't exist and it creates the directory before the file is created. This is highlighted in the PR description notes.

"directories": {
 "logs": "/tmp/eq-logs"
}
  Zone |    Info    | StartFileLogs Logs directory not found, creating [/tmp/eq-logs] 
  Zone |    Info    | StartFileLogs Starting File Log [/tmp/eq-logs/zone/zone_5448.log] 

@Akkadius Akkadius marked this pull request as ready for review January 6, 2025 05:37
@Akkadius Akkadius merged commit 8a7d5e7 into master Jan 6, 2025
2 checks passed
@Akkadius Akkadius deleted the akkadius/path-manager-improvements branch January 6, 2025 05:44
@Akkadius Akkadius mentioned this pull request Jan 6, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants