Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions include/commands.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#ifndef COMMANDS_H
#define COMMANDS_H

#include "globals.h"
#include "packets.h"
#include "procedures.h"

char *getNextArgument ();
char *getRemainingArguments ();
void handleCommand (PlayerData *sender, int message_len);
#if MAX_WHITELISTED_PLAYERS > 0
void handleWhitelistCommand (PlayerData *sender);
#endif
void handleMessageCommand (PlayerData *sender);
void handleHelpCommand (PlayerData *sender);

#endif
8 changes: 8 additions & 0 deletions include/globals.h
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,9 @@
// If defined, players are able to receive damage from nearby cacti.
#define ENABLE_CACTUS_DAMAGE

// If set to a number greater than 0, enables whitelist commands and functionality to prevent automated server scraping.
#define MAX_WHITELISTED_PLAYERS 0

// If defined, logs unrecognized packet IDs
// #define DEV_LOG_UNKNOWN_PACKETS

Expand Down Expand Up @@ -186,6 +189,11 @@ extern uint8_t motd_len;
extern uint8_t brand_len;
#endif

#if MAX_WHITELISTED_PLAYERS > 0
extern uint8_t enforce_whitelist;
extern char whitelisted_players[MAX_WHITELISTED_PLAYERS][16];
#endif

extern uint16_t client_count;

typedef struct {
Expand Down
9 changes: 8 additions & 1 deletion include/procedures.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,16 @@ int getClientState (int client_fd);
int getClientIndex (int client_fd);

void resetPlayerData (PlayerData *player);

#if MAX_WHITELISTED_PLAYERS > 0
uint8_t isPlayerWhitelisted (char *name);
uint8_t addPlayerToWhitelist (char *name);
uint8_t removePlayerFromWhitelist (char *name);
#endif

int reservePlayerData (int client_fd, uint8_t *uuid, char* name);
int getPlayerData (int client_fd, PlayerData **output);
PlayerData *getPlayerByName (int start_offset, int end_offset, uint8_t *buffer);
PlayerData *getPlayerByName (char *name, uint8_t *buffer);
void handlePlayerDisconnect (int client_fd);
void handlePlayerJoin (PlayerData* player);
void disconnectClient (int *client_fd, int cause);
Expand Down
173 changes: 173 additions & 0 deletions src/commands.c
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Add spaces after if tokens, before opening bracket.

Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
#include <stdio.h>
#include <string.h>

#include "globals.h"
#include "commands.h"

void handleCommand (PlayerData *player, int message_len) {
char* command = strtok((char *)recv_buffer, " ");
if (!strcmp(command, "!help")) {
handleHelpCommand(player);
} else if (!strcmp(command, "!msg")) {
handleMessageCommand(player);
#if MAX_WHITELISTED_PLAYERS > 0
} else if (!strcmp(command, "!whitelist")) {
handleWhitelistCommand(player);
#endif
} else {
sc_systemChat(player->client_fd, "§7Unknown command. Type !help for help.", 40);
}
}

// Pulls a single space-delimited argument from the chat buffer
char *getNextArgument () {
return strtok(NULL, " ");
}

// Pulls the remaining text from the chat buffer
char *getRemainingArguments () {
return strtok(NULL, "");
}

#if MAX_WHITELISTED_PLAYERS > 0
void handleWhitelistCommand (PlayerData *player) {
char *first_arg = getNextArgument();

if (first_arg == NULL) {
goto usage;
}

if (!strcmp(first_arg, "on")) {
sc_systemChat(player->client_fd, "§aWhitelist has been enabled", 29);
enforce_whitelist = 1;
return;
} else if (!strcmp(first_arg, "off")) {
sc_systemChat(player->client_fd, "§cWhitelist has been disabled", 30);
enforce_whitelist = 0;
return;
} else if (!strcmp(first_arg, "add")) {
char* player_arg = getNextArgument();
if (player_arg == NULL) {
goto usage;
}
int player_len = strlen(player_arg);
if (player_len < 3 || player_len > 16) {
sc_systemChat(player->client_fd, "§cError: input username is invalid", 35);
return;
}
int result = addPlayerToWhitelist(player_arg);
if (result == 0) {
sc_systemChat(player->client_fd, "§7Successfully added player to the whitelist", 45);
} else if (result == 1) {
sc_systemChat(player->client_fd, "§cError: Player is already on the whitelist", 44);
} else {
sc_systemChat(player->client_fd, "§cError: The whitelist is full, remove other players first then try again", 74);
}
return;
} else if (!strcmp(first_arg, "remove")) {
char* player_arg = getNextArgument();
if (player_arg == NULL) {
goto usage;
}
int player_len = strlen(player_arg);
if (player_len < 3 || player_len > 16) {
sc_systemChat(player->client_fd, "§cError: input username is invalid", 35);
return;
}
if (removePlayerFromWhitelist(player_arg) == 0) {
sc_systemChat(player->client_fd, "§7Successfully removed player from the whitelist", 49);
} else {
sc_systemChat(player->client_fd, "§cError: Player is not on the whitelist", 40);
}
return;
} else if (!strcmp(first_arg, "list")) {
snprintf((char *)recv_buffer, sizeof(recv_buffer), "§7The currently whitelisted players are:");
recv_buffer[41] = ' ';
int length = 42;
int totalPlayers = 0;
for (int i = 0; i < MAX_WHITELISTED_PLAYERS; i ++) {
if (whitelisted_players[i][0] == '\0') continue;

totalPlayers++;

// If we run out of buffer space, then send what we have and loop back to the beginning of the buffer
if (length >= MAX_RECV_BUF_LEN - 18) {
printf("flushing buffer: len=%d, '%s'\n", length, recv_buffer);
sc_systemChat(player->client_fd, (char *)recv_buffer, length);
// Set beginning of buffer to '§7' to add chat color to next chunk
recv_buffer[0] = 0xC2;
recv_buffer[1] = 0xA7;
recv_buffer[2] = '7';
length = 3;
}

snprintf((char *)recv_buffer + length, sizeof(recv_buffer) - length, "%s, ", whitelisted_players[i]);
length += strlen(whitelisted_players[i]) + 2;
}
if (totalPlayers == 0) {
sc_systemChat(player->client_fd, "§7There are currently no whitelisted players", 45);
return;
}
// Subtract 2 from the length to remove the trailing comma and space
sc_systemChat(player->client_fd, (char *)recv_buffer, length - 2);
return;
}
usage:
sc_systemChat(player->client_fd, "§7Usage: !whitelist <on|off|add|remove> [username]", 51);
}
#endif

void handleMessageCommand (PlayerData* player) {
char* target_name = getNextArgument();

// Send usage guide if arguments are missing
if (target_name == NULL) goto usage;

// Query the target player
PlayerData *target = getPlayerByName(target_name, recv_buffer);
if (target == NULL) {
sc_systemChat(player->client_fd, "Player not found", 16);
return;
}

char *message = getRemainingArguments();

// Don't send empty messages
if (message == NULL) goto usage;

// Format output as a vanilla whisper
int name_len = strlen(player->name);
int text_len = strlen(message);
memmove(recv_buffer + name_len + 24, message, text_len);
snprintf((char *)recv_buffer, sizeof(recv_buffer), "§7§o%s whispers to you:", player->name);

// snprintf always null terminates strings, so we get rid of that here
recv_buffer[name_len + 23] = ' ';

// Send message to target player
sc_systemChat(target->client_fd, (char *)recv_buffer, (uint16_t)(name_len + 24 + text_len));

// Format output for sending player
int target_name_len = strlen(target->name);
memmove(recv_buffer + target_name_len + 23, recv_buffer + name_len + 24, text_len);
snprintf((char *)recv_buffer, sizeof(recv_buffer), "§7§oYou whisper to %s:", target->name);
recv_buffer[target_name_len + 22] = ' ';

// Report back to sending player
sc_systemChat(player->client_fd, (char *)recv_buffer, (uint16_t)(target_name_len + 23 + text_len));
return;

usage:
sc_systemChat(player->client_fd, "§7Usage: !msg <player> <message>", 33);
}

void handleHelpCommand (PlayerData *player) {
// Send command guide
const char help_msg[] = "§7Commands:\n"
" !msg <player> <message> - Send a private message\n"
#if MAX_WHITELISTED_PLAYERS > 0
" !whitelist <on|off|add|remove> [username]\n"
#endif
" !help - Show this help message";
sc_systemChat(player->client_fd, (char *)help_msg, (uint16_t)sizeof(help_msg) - 1);
}
5 changes: 5 additions & 0 deletions src/globals.c
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@ uint8_t motd_len = sizeof(motd) - 1;
uint8_t brand_len = sizeof(brand) - 1;
#endif

#if MAX_WHITELISTED_PLAYERS > 0
uint8_t enforce_whitelist = 0;
char whitelisted_players[MAX_WHITELISTED_PLAYERS][16];
#endif

uint16_t client_count;

BlockChange block_changes[MAX_BLOCK_CHANGES];
Expand Down
7 changes: 7 additions & 0 deletions src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,13 @@ void handlePacket (int client_fd, int length, int packet_id, int state) {
uint8_t uuid[16];
char name[16];
if (cs_loginStart(client_fd, uuid, name)) break;
#if MAX_WHITELISTED_PLAYERS > 0
if (enforce_whitelist && !isPlayerWhitelisted(name)) {
printf("Disconnecting client %d because they are not on the whitelist\n", client_fd);
recv_count = 0;
return;
}
#endif
if (reservePlayerData(client_fd, uuid, name)) {
recv_count = 0;
return;
Expand Down
63 changes: 2 additions & 61 deletions src/packets.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "crafting.h"
#include "procedures.h"
#include "packets.h"
#include "commands.h"

// S->C Status Response (server list ping)
int sc_statusResponse (int client_fd) {
Expand Down Expand Up @@ -1143,67 +1144,7 @@ int cs_chat (int client_fd) {
goto cleanup;
}

// Handle chat commands

if (!strncmp((char *)recv_buffer, "!msg", 4)) {

int target_offset = 5;
int target_end_offset = 0;
int text_offset = 0;

// Skip spaces after "!msg"
while (recv_buffer[target_offset] == ' ') target_offset++;
target_end_offset = target_offset;
// Extract target name
while (recv_buffer[target_end_offset] != ' ' && recv_buffer[target_end_offset] != '\0' && target_end_offset < 21) target_end_offset++;
text_offset = target_end_offset;
// Skip spaces before message
while (recv_buffer[text_offset] == ' ') text_offset++;

// Send usage guide if arguments are missing
if (target_offset == target_end_offset || target_end_offset == text_offset) {
sc_systemChat(client_fd, "§7Usage: !msg <player> <message>", 33);
goto cleanup;
}

// Query the target player
PlayerData *target = getPlayerByName(target_offset, target_end_offset, recv_buffer);
if (target == NULL) {
sc_systemChat(client_fd, "Player not found", 16);
goto cleanup;
}

// Format output as a vanilla whisper
int name_len = strlen(player->name);
int text_len = message_len - text_offset;
memmove(recv_buffer + name_len + 24, recv_buffer + text_offset, text_len);
snprintf((char *)recv_buffer, sizeof(recv_buffer), "§7§o%s whispers to you:", player->name);
recv_buffer[name_len + 23] = ' ';
// Send message to target player
sc_systemChat(target->client_fd, (char *)recv_buffer, (uint16_t)(name_len + 24 + text_len));

// Format output for sending player
int target_len = target_end_offset - target_offset;
memmove(recv_buffer + target_len + 23, recv_buffer + name_len + 24, text_len);
snprintf((char *)recv_buffer, sizeof(recv_buffer), "§7§oYou whisper to %s:", target->name);
recv_buffer[target_len + 22] = ' ';
// Report back to sending player
sc_systemChat(client_fd, (char *)recv_buffer, (uint16_t)(target_len + 23 + text_len));

goto cleanup;
}

if (!strncmp((char *)recv_buffer, "!help", 5)) {
// Send command guide
const char help_msg[] = "§7Commands:\n"
" !msg <player> <message> - Send a private message\n"
" !help - Show this help message";
sc_systemChat(client_fd, (char *)help_msg, (uint16_t)sizeof(help_msg) - 1);
goto cleanup;
}

// Handle fall-through case
sc_systemChat(client_fd, "§7Unknown command", 18);
handleCommand(player, message_len);

cleanup:
readUint64(client_fd); // Ignore timestamp
Expand Down
Loading