Commit Diff


commit - ba86dfd927b5e2432be797e12095642dc4091fe1
commit + b23e19ecd4f7a33e6cd03ab53103b65ccf4f8f1d
blob - f3aa33133e0b13d3946824c2a4ebe4271a82ca63
blob + b7273c8c7da9c4d5ec7c63ddb234af001c60147d
--- include/procedures.h
+++ include/procedures.h
@@ -14,6 +14,7 @@ int getClientIndex (int client_fd);
 void resetPlayerData (PlayerData *player);
 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);
 void handlePlayerDisconnect (int client_fd);
 void handlePlayerJoin (PlayerData* player);
 void disconnectClient (int *client_fd, int cause);
blob - 5cec9d5f195917440195a5cd8a89e4ab4da15f79
blob + cbb110903212a3c5afd085aa3c8cdc688d50d4cc
--- src/packets.c
+++ src/packets.c
@@ -1131,29 +1131,95 @@ int cs_chat (int client_fd) {
     message_len = 224;
   }
 
-  // Shift message contents forward to make space for player name tag
-  memmove(recv_buffer + name_len + 3, recv_buffer, message_len + 1);
-  // Copy player name to index 1
-  memcpy(recv_buffer + 1, player->name, name_len);
-  // Surround player name with brackets and a space
-  recv_buffer[0] = '<';
-  recv_buffer[name_len + 1] = '>';
-  recv_buffer[name_len + 2] = ' ';
+  if (recv_buffer[0] != '!') { // Standard chat message
 
-  // Forward message to all connected players
-  for (int i = 0; i < MAX_PLAYERS; i ++) {
-    if (player_data[i].client_fd == -1) continue;
-    if (player_data[i].flags & 0x20) continue;
-    sc_systemChat(player_data[i].client_fd, (char *)recv_buffer, message_len + name_len + 3);
+    // Shift message contents forward to make space for player name tag
+    memmove(recv_buffer + name_len + 3, recv_buffer, message_len + 1);
+    // Copy player name to index 1
+    memcpy(recv_buffer + 1, player->name, name_len);
+    // Surround player name with brackets and a space
+    recv_buffer[0] = '<';
+    recv_buffer[name_len + 1] = '>';
+    recv_buffer[name_len + 2] = ' ';
+
+    // Forward message to all connected players
+    for (int i = 0; i < MAX_PLAYERS; i ++) {
+      if (player_data[i].client_fd == -1) continue;
+      if (player_data[i].flags & 0x20) continue;
+      sc_systemChat(player_data[i].client_fd, (char *)recv_buffer, message_len + name_len + 3);
+    }
+
+    goto cleanup;
   }
 
+  // Handle chat commands
+
+  if (!strncmp(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(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(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(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);
+
+cleanup:
   readUint64(client_fd); // Ignore timestamp
   readUint64(client_fd); // Ignore salt
-
   // Ignore signature (if any)
   uint8_t has_signature = readByte(client_fd);
   if (has_signature) recv_all(client_fd, recv_buffer, 256, false);
-
   readVarInt(client_fd); // Ignore message count
   // Ignore acknowledgement bitmask and checksum
   recv_all(client_fd, recv_buffer, 4, false);
blob - 4048bba6d0431bab6dc4aa1f20f317ff3e957ab6
blob + 7bc5d9d1280909b40a64044118715539d0e40972
--- src/procedures.c
+++ src/procedures.c
@@ -125,6 +125,22 @@ int getPlayerData (int client_fd, PlayerData **output)
   return 1;
 }
 
+// Returns the player with the given name, or NULL if not found
+PlayerData *getPlayerByName (int start_offset, int end_offset, uint8_t *buffer) {
+  for (int i = 0; i < MAX_PLAYERS; i ++) {
+    if (player_data[i].client_fd == -1) continue;
+    int j;
+    for (j = start_offset; j < end_offset && j < 256 && buffer[j] != ' '; j++) {
+      if (player_data[i].name[j - start_offset] != buffer[j]) break;
+    }
+    if ((j == end_offset || buffer[j] == ' ') && j < 256) {
+      return &player_data[i];
+    }
+  }
+  return NULL;
+}
+
+
 // Marks a client as disconnected and cleans up player data
 void handlePlayerDisconnect (int client_fd) {
   // Search for a corresponding player in the player data array