Skip to main content

Readability

Key Principles of Readable Code

Keep similar functionality in the same section of your file. For example, group all event hooks, commands, and helper methods separately to avoid confusion. For small plugins, comments can be more than enough to help you keep your sections straight.

❌ **Bad Example:**
private void OnPlayerConnected(BasePlayer player)
{
Puts($"{player.displayName} joined.");
}

[ChatCommand("goodbye")]
private void GoodbyeCommand(BasePlayer player, string command, string[] args)
{
player.ChatMessage("Goodbye, " + player.displayName + "!");
}

private void MyHelperFunction() // Unrelated function placed randomly
{
Puts("Helper function running.");
}

[ChatCommand("hello")]
private void HelloCommand(BasePlayer player, string command, string[] args)
{
player.ChatMessage("Hello, " + player.displayName + "!");
}

Good Example:

✅ **Good Example:**
// Hooks
private void OnPlayerConnected(BasePlayer player)
{
Puts($"{player.displayName} joined.");
}

// Chat Commands
[ChatCommand("hello")]
private void HelloCommand(BasePlayer player, string command, string[] args)
{
player.ChatMessage("Hello, " + player.displayName + "!");
}

[ChatCommand("goodbye")]
private void GoodbyeCommand(BasePlayer player, string command, string[] args)
{
player.ChatMessage("Goodbye, " + player.displayName + "!");
}

// Helper Functions
private void MyHelperFunction()
{
Puts("Helper function running.");
}

Use #region to Organize Large Files

For larger plugins, use #region and #endregion to separate different sections and improve navigation.

Example:

#region Hooks
private void OnPlayerInit(BasePlayer player)
{
Puts($"{player.displayName} joined.");
}
#endregion

#region Commands
[ChatCommand("hello")]
private void HelloCommand(BasePlayer player, string command, string[] args)
{
player.ChatMessage("Hello, " + player.displayName + "!");
}
#endregion

#region Helpers
private void SendMessage(BasePlayer player, string message)
{
player.ChatMessage(message);
}
#endregion

Most modern IDEs allow you to collapse/expand regions, making it easier to navigate large code files.


Avoid Unnecessary Nesting

Excessive nesting makes code harder to read and debug. Flatten your code when possible.

Bad Example:

private void CheckPlayerActive(BasePlayer player)
{
if (player != null)
{
if (player.IsAlive())
{
if (!player.IsSleeping())
{
Puts($"{player.displayName} is active.");
}
}
}
}

Good Example:

private void CheckPlayerActive(BasePlayer player)
{
if (player == null) return;
if (!player.IsAlive() || player.IsSleeping()) return;

Puts($"{player.displayName} is active.");
}

Reducing unnecessary nesting makes the logic clearer and easier to follow.


Use Descriptive Names for Variables and Methods

Names should describe what the function or variable does, not just its type or purpose.

Bad Example:

private void DoStuff()
{
var p = "Test";
var x = GetData();
}

Good Example:

private void SendWelcomeMessage()
{
string welcomeText = "Welcome to the server!";
string playerData = GetPlayerData();
}

This makes it much easier to understand what the code does at a glance.


Keep Functions Short and Focused

Each function should do one thing well. If a function is too long or hard to read, break it into smaller helper functions. This will allow you to keep your code readable while not losing functionality. With this in mind, a lot of developers only create additional functions if those functions will be reused somewhere else.

Bad Example:

private void HandlePlayerJoin(BasePlayer player)
{
if (player == null) return;
Puts($"{player.displayName} joined.");
File.AppendAllText("logs.txt", $"{DateTime.UtcNow}: {player.UserIDString}\n");
player.ChatMessage("Welcome!");
if (!player.HasPermission("vip")) player.ChatMessage("Upgrade to VIP!");
foreach (var admin in BasePlayer.activePlayerList)
if (admin.IsAdmin) admin.ChatMessage($"[Admin Alert] {player.displayName} joined.");
if (player.displayName.Contains("admin")) player.Kick("Suspicious name.");
}
Don't just copy the code below

The example below is a good example of improving readability, but the functions are bullshit.

Good Example:

private void HandlePlayerJoin(BasePlayer player)
{
if (player == null) return;
LogPlayerJoin(player);
SendWelcomeMessage(player);
NotifyAdmins(player);
CheckForSuspiciousNames(player);
}

private void LogPlayerJoin(BasePlayer player)
{
Puts($"{DateTime.UtcNow}: {player.UserIDString}");
}

private void SendWelcomeMessage(BasePlayer player)
{
player.ChatMessage("Welcome!");
}

private void NotifyAdmins(BasePlayer player)
{
if (!player.IsAdmin) return;
player.ChatMessage($"[Admin] {player.displayName}");
}

private void CheckForSuspiciousNames(BasePlayer player)
{
if (player.displayName.Contains("admin")) player.Kick("Suspicious name.");
}

Each function now has a clear responsibility, making the code easier to maintain.


Summary

✔️ Group similar functions together (e.g., hooks, commands, helpers).
✔️ Use #region to separate sections in larger plugins.
✔️ Avoid deep nesting by using early returns.
✔️ Use descriptive variable and function names to improve clarity.
✔️ Keep functions short and focused for better maintainability.

By following these best practices, your code will be easier to read, debug, and extend! 🚀