129 lines
5.2 KiB
C++
129 lines
5.2 KiB
C++
#include <string>
|
|
#include <cstring>
|
|
#include <cstdio>
|
|
#include <vector>
|
|
#include <pthread.h>
|
|
|
|
#include <libreborn/libreborn.h>
|
|
#include <libreborn/minecraft.h>
|
|
|
|
#include "../init/init.h"
|
|
#include "../feature/feature.h"
|
|
#include "../input/input.h"
|
|
#include "chat.h"
|
|
|
|
// Store If Chat is Enabled
|
|
int _chat_enabled = 0;
|
|
|
|
// Message Limitations
|
|
#define MAX_CHAT_MESSAGE_LENGTH 512
|
|
|
|
// Send API Command
|
|
static void send_api_command(unsigned char *minecraft, char *str) {
|
|
struct ConnectedClient client;
|
|
client.sock = -1;
|
|
client.str = "";
|
|
client.time = 0;
|
|
unsigned char *command_server = *(unsigned char **) (minecraft + Minecraft_command_server_property_offset);
|
|
if (command_server != NULL) {
|
|
(*CommandServer_parse)(command_server, client, str);
|
|
}
|
|
}
|
|
// Send API Chat Command
|
|
static void send_api_chat_command(unsigned char *minecraft, char *str) {
|
|
char *command = NULL;
|
|
safe_asprintf(&command, "chat.post(%s)\n", str);
|
|
send_api_command(minecraft, command);
|
|
free(command);
|
|
}
|
|
|
|
// Send Message To Players
|
|
static void send_message(unsigned char *server_side_network_handler, char *username, char *message) {
|
|
char *full_message = NULL;
|
|
safe_asprintf(&full_message, "<%s> %s", username, message);
|
|
sanitize_string(&full_message, MAX_CHAT_MESSAGE_LENGTH, 0);
|
|
(*ServerSideNetworkHandler_displayGameMessage)(server_side_network_handler, std::string(full_message));
|
|
free(full_message);
|
|
}
|
|
|
|
// Manually Send (And Loopback) ChatPacket
|
|
static void CommandServer_parse_CommandServer_dispatchPacket_injection(unsigned char *command_server, unsigned char *packet) {
|
|
unsigned char *minecraft = *(unsigned char **) (command_server + CommandServer_minecraft_property_offset);
|
|
if (minecraft != NULL) {
|
|
unsigned char *rak_net_instance = *(unsigned char **) (minecraft + Minecraft_rak_net_instance_property_offset);
|
|
unsigned char *rak_net_instance_vtable = *(unsigned char **) rak_net_instance;
|
|
RakNetInstance_isServer_t RakNetInstance_isServer = *(RakNetInstance_isServer_t *) (rak_net_instance_vtable + RakNetInstance_isServer_vtable_offset);
|
|
if ((*RakNetInstance_isServer)(rak_net_instance)) {
|
|
// Hosting Multiplayer
|
|
char *message = *(char **) (packet + ChatPacket_message_property_offset);
|
|
unsigned char *server_side_network_handler = *(unsigned char **) (minecraft + Minecraft_network_handler_property_offset);
|
|
send_message(server_side_network_handler, *default_username, message);
|
|
} else {
|
|
// Client
|
|
RakNetInstance_send_t RakNetInstance_send = *(RakNetInstance_send_t *) (rak_net_instance_vtable + RakNetInstance_send_vtable_offset);
|
|
(*RakNetInstance_send)(rak_net_instance, packet);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Handle ChatPacket Server-Side
|
|
static void ServerSideNetworkHandler_handle_ChatPacket_injection(unsigned char *server_side_network_handler, unsigned char *rak_net_guid, unsigned char *chat_packet) {
|
|
unsigned char *player = (*ServerSideNetworkHandler_getPlayer)(server_side_network_handler, rak_net_guid);
|
|
if (player != NULL) {
|
|
char *username = *(char **) (player + Player_username_property_offset);
|
|
char *message = *(char **) (chat_packet + ChatPacket_message_property_offset);
|
|
send_message(server_side_network_handler, username, message);
|
|
}
|
|
}
|
|
|
|
// Message Queue
|
|
static pthread_mutex_t queue_mutex = PTHREAD_MUTEX_INITIALIZER;
|
|
static std::vector<std::string> queue;
|
|
// Add To Queue
|
|
void _chat_queue_message(char *message) {
|
|
// Lock
|
|
pthread_mutex_lock(&queue_mutex);
|
|
// Add
|
|
std::string str;
|
|
str.append(message);
|
|
queue.push_back(str);
|
|
// Unlock
|
|
pthread_mutex_unlock(&queue_mutex);
|
|
}
|
|
// Empty Queue
|
|
unsigned int old_chat_counter = 0;
|
|
static void send_queued_messages(unsigned char *minecraft) {
|
|
// Lock
|
|
pthread_mutex_lock(&queue_mutex);
|
|
// If Message Was Submitted, No Other Chat Windows Are Open, And The Game Is Not Paused, Then Re-Lock Cursor
|
|
unsigned int new_chat_counter = chat_get_counter();
|
|
if (old_chat_counter > new_chat_counter && new_chat_counter == 0 && (*(unsigned char **) (minecraft + Minecraft_screen_property_offset)) == NULL) {
|
|
// Grab Mouse
|
|
input_set_mouse_grab_state(-1);
|
|
}
|
|
old_chat_counter = new_chat_counter;
|
|
// Loop
|
|
for (unsigned int i = 0; i < queue.size(); i++) {
|
|
send_api_chat_command(minecraft, (char *) queue[i].c_str());
|
|
}
|
|
queue.clear();
|
|
// Unlock
|
|
pthread_mutex_unlock(&queue_mutex);
|
|
}
|
|
|
|
// Init
|
|
void init_chat() {
|
|
_chat_enabled = feature_has("Implement Chat", 1);
|
|
if (_chat_enabled) {
|
|
// Disable Original ChatPacket Loopback
|
|
unsigned char disable_chat_packet_loopback_patch[4] = {0x00, 0xf0, 0x20, 0xe3}; // "nop"
|
|
patch((void *) 0x6b490, disable_chat_packet_loopback_patch);
|
|
// Manually Send (And Loopback) ChatPacket
|
|
overwrite_call((void *) 0x6b518, (void *) CommandServer_parse_CommandServer_dispatchPacket_injection);
|
|
// Re-Broadcast ChatPacket
|
|
patch_address(ServerSideNetworkHandler_handle_ChatPacket_vtable_addr, (void *) ServerSideNetworkHandler_handle_ChatPacket_injection);
|
|
// Send Messages On Input Tick
|
|
input_run_on_tick(send_queued_messages);
|
|
}
|
|
}
|