Skip to content
Open
Show file tree
Hide file tree
Changes from 4 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
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -154,3 +154,4 @@ endif()
include_directories(${CMAKE_BINARY_DIR}/generated) # Globally include the build/generated directory

add_subdirectory(src)
add_subdirectory(tools)
20 changes: 20 additions & 0 deletions tools/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
add_executable(announce announce.cpp)

set_target_properties(announce PROPERTIES
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/tools
)

target_include_directories(announce PRIVATE
${CMAKE_SOURCE_DIR}/src
${CMAKE_SOURCE_DIR}/src/map
${CMAKE_BINARY_DIR}/generated
${ZeroMQ_INCLUDE_DIRS}
)

target_link_libraries(announce
PRIVATE
project_options
project_warnings
xi_world_lib
${ZeroMQ_LIBRARIES}
)
60 changes: 60 additions & 0 deletions tools/announce.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
#include <cstring>
#include <iostream>
#include <string>

#include <zmq.hpp>
#include <arpa/inet.h>

#include "common/ipc.h"
#include "common/ipp.h"
#include "map/enums/chat_message_type.h"

int main(int argc, char** argv)
{
if (argc < 2)
{
std::cerr << "Usage: announce \"message...\"\n";
return 2;
}

std::string msg;
for (int i = 1; i < argc; ++i)
{
if (i > 1) msg += " ";
msg += argv[i];
}

// Build the IPC message as expected by ipc::fromBytesWithHeader
ipc::ChatMessageServerMessage m{};
m.senderId = 0;
m.senderName = "";
m.message = msg;
m.zoneId = 0;
m.gmLevel = 1;
m.messageType = CHAT_MESSAGE_TYPE::MESSAGE_SYSTEM_1; // = 6 for system message

const auto payload = ipc::toBytesWithHeader(m);

// xi_world listens on 127.0.0.1:54003
const std::string endpoint = "tcp://127.0.0.1:54003";

// Match the routing format that the server expects
in_addr addr{};
inet_aton("127.0.0.1", &addr);
const uint32 ip = ntohl(addr.s_addr); // host-order uint32
IPP ipp(ip, 54003);

zmq::context_t ctx(1);
zmq::socket_t sock(ctx, zmq::socket_type::dealer);

auto rid_msg = ipp.toZMQMessage();
sock.set(zmq::sockopt::routing_id, rid_msg.to_string());
sock.connect(endpoint);

zmq::message_t out(payload.size());
std::memcpy(out.data(), payload.data(), payload.size());
sock.send(out, zmq::send_flags::none);

std::cout << "OK\n";
return 0;
}
114 changes: 17 additions & 97 deletions tools/announce.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,107 +9,27 @@
# pip3 install zmq pyzmq
#
#############################

import socket
import os
import sys
import zmq
import struct

context = zmq.Context()
sock = context.socket(zmq.DEALER)

ip_str = "127.0.0.1"
port = 54003

ip_bytes = socket.inet_aton(ip_str)
(ip_int,) = struct.unpack("!I", ip_bytes)
ipp = ip_int | (port << 32)
ipp_bytes = struct.pack("!Q", ipp)

print(f"Connecting to endpoint: {ip_str}:{port}")

sock.setsockopt(zmq.ROUTING_ID, ipp_bytes)
sock.connect("tcp://127.0.0.1:54003")


def print_help():
print("You must provide a message to send.")
print("Example:")
print('python3 .\\announce.py "Here is a message from python!"')


def build_chat_packet(gm_flag, zone, sender, msg):
buff_size = min(236, len(sender) + len(msg) + 10)
buffer = bytearray(buff_size)

if sender is None:
sender = ""

# alpaca encoding for:
#
# ChatMessageServerMessage = 11;
#
# struct ChatMessageServerMessage
# {
# uint32 senderId{};
# std::string senderName{};
# std::string message{};
# uint16 zoneId{};
# uint8 gmLevel{};
# };

idx = 0

# ChatMessageServerMessage
buffer[idx] = 11
idx += 1

# senderId
buffer[idx] = 0
idx += 1

# len(senderName)
buffer[idx] = len(sender)
idx += 1

# senderName
for i, c in enumerate(sender):
buffer[idx] = ord(c)
idx += 1

# len(message)
buffer[idx] = len(msg)
idx += 1

# message
for i, c in enumerate(msg):
buffer[idx] = ord(c)
idx += 1

# zoneId
buffer[idx] = zone
idx += 1

# gmLevel
buffer[idx] = gm_flag
idx += 1

return buffer


def send_server_message(msg):
print(f"Sending '{msg}'")
buffer = build_chat_packet(1, 0, "", msg)
sock.send(buffer)
import subprocess

if len(sys.argv) < 2:
print('Usage: announce.py "message..."', file=sys.stderr)
sys.exit(2)

def main():
if len(sys.argv) < 2:
print_help()
return
# Absolute path to this script's directory
HERE = os.path.dirname(os.path.abspath(__file__))

send_server_message(sys.argv[1])
# Native announce binary should live alongside this script
EXE = os.path.join(HERE, "announce")

if not (os.path.isfile(EXE) and os.access(EXE, os.X_OK)):
print("ERROR: announce binary not found.", file=sys.stderr)
print("Expected at:", EXE, file=sys.stderr)
print("Build with:", file=sys.stderr)
print(" cmake -S . -B build && cmake --build build --target announce", file=sys.stderr)
sys.exit(1)

if __name__ == "__main__":
msg = " ".join(sys.argv[1:])
subprocess.check_call([EXE, msg])
main()
Loading