Bukkit - Send data back to people connected through RCON - bukkit

I have a NodeJS / SocketIO app which connects to my Minecraft server through RCON protocol and it works perfectly, keep the connection open and listens for any kind of data which is retrieved.
Example, if I type a command which isn't available, it will respond with a message.
Now I'm trying to whenever any player on the Minecraft server chats, my Bukkit plugin will take that message and send it through to any connected on RCON.
This is part of my Bukkit plugin which fires when the player chats.
#EventHandler
public void onPlayerChat( AsyncPlayerChatEvent e ) {
Bukkit.getLogger().info("Test 1");
this.getLogger().info("Test 2");
Bukkit.getServer().getConsoleSender().sendMessage("Test 3");
this.getServer().getConsoleSender().sendMessage("Test 4");
}
The messages are recorded in the server log, though I do not get anything back through the rcon protocol.

RCON used by minecraft does not have a mechanism to send messages back after the initial packets. If you need a mechanism like this, you need to invent your own protocol, or make a command that returns the last chat log if it's executed.
To make this command, we can simple make a Queue<String> where we are putting our messages (like chat) into.
final Queue<String> messages = new ArrayDeque<>(64);
public void insertMessage(String message) {
synchronized (messages) {
messages.add(message);
if (messages.size() == 64) // full
messages.remove();
}
}
We can then hook up this method to our chat event:
#EventHandler(priority=EventPriority.MONITOR,ignoreCancelled=true)
public void onChat(AsyncPlayerChatEvent evt) {
insertMessage("[CHAT] " + evt.getPlayer() + " : " + evt.getMessage());
}
Then we only need to make a command to read out our messages list, this is easy to make:
private String getMessage() {
synchronized (messages) {
return messages.poll();
}
}
#Override
public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {
if(!cmd.testPermissions()) {
return true;
}
String message;
while((message = getMessage()) != null) {
sender.sendMessage(message);
}
}
This allows you to get the server log from rcon by just typing in a command or making your client application so that it executes this command every second or so.

Related

How to use grpc c++ ClientAsyncReader<Message> for server side streams

I am using a very simple proto where the Message contains only 1 string field. Like so:
service LongLivedConnection {
// Starts a grpc connection
rpc Connect(Connection) returns (stream Message) {}
}
message Connection{
string userId = 1;
}
message Message{
string serverMessage = 1;
}
The use case is that the client should connect to the server, and the server will use this grpc for push messages.
Now, for the client code, assuming that I am already in a worker thread, how do I properly set it up so that I can continuously receive messages that come from server at random times?
void StartConnection(const std::string& user) {
Connection request;
request.set_userId(user);
Message message;
ClientContext context;
stub_->Connect(&context, request, &reply);
// What should I do from now on?
// notify(serverMessage);
}
void notify(std::string message) {
// generate message events and pass to main event loop
}
I figured out how to used the api. Looks like it is pretty flexible, but still a little bit weird given that I typically just expect the async api to receive some kind of lambda callback.
The code below is blocking, you'll have to run this in a different thread so it doesn't block your application.
I believe you can have multiple thread accessing the CompletionQueue, but in my case I just had one single thread handling this grpc connection.
GrpcConnection.h file:
public:
void StartGrpcConnection();
private:
std::shared_ptr<grpc::Channel> m_channel;
std::unique_ptr<grpc::ClientReader<push_notifications::Message>> m_reader;
std::unique_ptr<push_notifications::PushNotificationService::Stub> m_stub;
GrpcConnection.cpp files:
...
void GrpcConnectionService::StartGrpcConnection()
{
m_channel = grpc::CreateChannel("localhost:50051",grpc::InsecureChannelCredentials());
LongLiveConnection::Connect request;
request.set_user_id(12345);
m_stub = LongLiveConnection::LongLiveConnectionService::NewStub(m_channel);
grpc::ClientContext context;
grpc::CompletionQueue cq;
std::unique_ptr<grpc::ClientAsyncReader<LongLiveConnection::Message>> reader =
m_stub->PrepareAsyncConnect(&context, request, &cq);
void* got_tag;
bool ok = false;
LongLiveConnection::Message reply;
reader->StartCall((void*)1);
cq.Next(&got_tag, &ok);
if (ok && got_tag == (void*)1)
{
// startCall() is successful if ok is true, and got_tag is void*1
// start the first read message with a different hardcoded tag
reader->Read(&reply, (void*)2);
while (true)
{
ok = false;
cq.Next(&got_tag, &ok);
if (got_tag == (void*)2)
{
// this is the message from server
std::string body = reply.server_message();
// do whatever you want with body, in my case i push it to my applications' event stream to be processed by other components
// lastly, initialize another read
reader->Read(&reply, (void*)2);
}
else if (got_tag == (void*)3)
{
// if you do something else, such as listening to GRPC channel state change, in your call, you can pass a different hardcoded tag, then, in here, you will be notified when the result is received from that call.
}
}
}
}

Why does QDialog::exec() blocks QTcpSocket work

I make a Bomberman (or DynaBlast) game clone with multiplayer. Server game and client game communicate through messages using QTcpSocket. Typical workflow for playing network game is following:
Server player execs NetworkGame dialog, among other things this dialog creates NetworkGame object
Client player execs ClientGame dialog, among other things this dialog creates ClientGame object
Client chooses IP of server and clicks "Connect"
Server accepts connections, server and clients are now able to send messages each other
Client sends "ready"-message, server start game
When game over, the game object emits GameStatusChanged(GameStatus newStatus) signal. This signal connected to MainWindow, which execs GameOverDialog. If player chooses "Play again" at GameOverDialog, MainWindow execs NetworkGame or ClientGame dialogs again and we are at the first points.
So, after first game is over, second exec of ClientGameDialog blocks QTcpSocket work in the way it cann't read data or emit QTcpSocket::readyRead signal (I don't know which one point exactly). ClientGameDialog's GUI is responsive, it can send messages to server, but it cann't read messages. At the same time NetworkGame and NetworkGameDialog work properly - they are able to send and receive messages. I checked all my classes several times and don't see any significant difference.
I think full code is to huge to post it here, so I gave UML a try. This is a chart for most important classes. Green arrow designates Qt's child-parent relations, starting at a child QObject it points to a parent.
When Socket class receives new message through QTcpSocket interface, it emits messageReceived(const Message& message) signal; other classes can connect to this signal via slots and handle messages. I don't see what Client, ServerWorker, Server classes can do with event loop, the just help to process raw data from QTcpSocket and deliver messages to other classes, particulary to Game and Dialog classes.
Here is some code (I have some code duplications, I leave them until better times). Creating game:
// Server game
void MainWindow::startNetworkGame() // User clicked "Start network game" button
{
const auto& player = mainMenuWidget_->selectedPlayer();
gameDialogs_ = createGameDialogs(this, GameType::Server, player);
auto answer = gameDialogs_.creationDialog->exec();
if (answer == QDialog::Accepted) {
auto initializationData = gameDialogs_.creationDialog->initializationData();
initializeGame(initializationData);
startGame(initializationData);
}
}
// Client game
void MainWindow::connectToServer() // User clicked "Connect to server" button
{
const auto& player = mainMenuWidget_->selectedPlayer();
gameDialogs_ = createGameDialogs(this, GameType::Client, player);
auto answer = gameDialogs_.creationDialog->exec(); // At first time it works fine
if (answer == QDialog::Accepted) {
auto initializationData = gameDialogs_.creationDialog->initializationData();
initializeGame(initializationData);
startGame(initializationData);
}
}
Next snippet is code for processing GameStatusChanged signal when game was over:
void MainWindow::gameStatusChanged(GameStatus newStatus)
{
if (newStatus == GameStatus::GameOver) {
auto* gameOverDialog = gameDialogs_.gameOverDialog;
gameOverDialog->setGameResult(gameData_.game->gameResult());
auto gameOverDialogAnswer = gameOverDialog->exec();
if (gameOverDialogAnswer == QDialog::Accepted) {
gameDialogs_.creationDialog->reset();
auto answer = gameDialogs_.creationDialog->exec(); // At this point client cann't receive messages, but server can.
if (answer == QDialog::Accepted) {
auto initializationData = gameDialogs_.creationDialog->initializationData();
initializeGame(initializationData);
startGame(initializationData);
} else {
showMainMenu();
}
} else {
showMainMenu();
}
}
}
I suspect that ClientGameDialog's event loop (is it indeed has it's own event loop) doesn't processes QTcpSocket's events. I tried to replace exec() with open methods for client dialog:
void MainWindow::gameStatusChanged(GameStatus newStatus)
{
if (newStatus == GameStatus::GameOver) {
auto* gameOverDialog = gameDialogs_.gameOverDialog;
gameOverDialog->setGameResult(gameData_.game->gameResult());
auto gameOverDialogAnswer = gameOverDialog->exec();
if (gameOverDialogAnswer == QDialog::Accepted) {
gameDialogs_.creationDialog->reset();
auto d = qobject_cast<ClientGameDialog*>(gameDialogs_.creationDialog);
if (d) {
gameDialogs_.creationDialog->open();
} else {
auto answer = gameDialogs_.creationDialog->exec();
if (answer == QDialog::Accepted) {
auto initializationData = gameDialogs_.creationDialog->initializationData();
initializeGame(initializationData);
startGame(initializationData);
} else {
showMainMenu();
}
}
} else {
showMainMenu();
}
}
}
It works, but I want to find where was the problem. Maybe someone can prompt, where to search a solution. Main questions are: where the difference between Server and Client code flows and why Client code works fine at the first time and breaks at the second.
This is because QDialog::exec is a synchronous blocking operation that will stop the event loop of the main thread and start a new event loop for this dialog. This most of the time is not a problem unless you are doing some continuous work on the main thread such as processing QTCPSocket
Instead of using QDialog::exec use QDialog::open which is asynchronous and does not start a new event loop, you can simply connect signals from the dialog to read the results once the user will accept/close the dialog.
If you require blocking dialogs then you also can simply offload QTcpSocket to another thread and do whole processing asynchronously and only emit required updates to the main GUI thread.

gRPC: What are the best practices for long-running streaming?

We've implemented a Java gRPC service that runs in the cloud, with an unidirectional (client to server) streaming RPC which looks like:
rpc PushUpdates(stream Update) returns (Ack);
A C++ client (a mobile device) calls this rpc as soon as it boots up, to continuously send an update every 30 or so seconds, perpetually as long as the device is up and running.
ChannelArguments chan_args;
// this will be secure channel eventually
auto channel_p = CreateCustomChannel(remote_addr, InsecureChannelCredentials(), chan_args);
auto stub_p = DialTcc::NewStub(channel_p);
// ...
Ack ack;
auto strm_ctxt_p = make_unique<ClientContext>();
auto strm_p = stub_p->PushUpdates(strm_ctxt_p.get(), &ack);
// ...
While(true) {
// wait until we are ready to send a new update
Update updt;
// populate updt;
if(!strm_p->Write(updt)) {
// stream is not kosher, create a new one and restart
break;
}
}
Now different kinds of network interruptions happen while this is happening:
the gRPC service running in the cloud may go down (for maintenance) or may simply become unreachable.
the device's own ip address keeps changing as it is a mobile device.
We've seen that on such events, neither the channel, nor the Write() API is able to detect network disconnection reliably. At times the client keep calling Write() (which doesn't return false) but the server doesn't receive any data (wireshark doesn't show any activity at the outgoing port of the client device).
What are the best practices to recover in such cases, so that the server starts receiving the updates within X seconds from the time when such an event occurs? It is understandable that there would loss of X seconds worth data whenever such an event happens, but we want to recover reliably within X seconds.
gRPC version: 1.30.2, Client: C++-14/Linux, Sever: Java/Linux
Here's how we've hacked this. I want to check if this can be made any better or anyone from gRPC can guide me about a better solution.
The protobuf for our service looks like this. It has an RPC for pinging the service, which is used frequently to test connectivity.
// Message used in IsAlive RPC
message Empty {}
// Acknowledgement sent by the service for updates received
message UpdateAck {}
// Messages streamed to the service by the client
message Update {
...
...
}
service GrpcService {
// for checking if we're able to connect
rpc Ping(Empty) returns (Empty);
// streaming RPC for pushing updates by client
rpc PushUpdate(stream Update) returns (UpdateAck);
}
Here is how the c++ client looks, which does the following:
Connect():
Create the stub for calling the RPCs, if the stub is nullptr.
Call Ping() in regular intervals until it is successful.
On success call PushUpdate(...) RPC to create a new stream.
On failure reset the stream to nullptr.
Stream(): Do the following a while(true) loop:
Get the update to be pushed.
Call Write(...) on the stream with the update to be pushed.
If Write(...) fails for any reason break and the control goes back to Connect().
Once in every 30 minutes (or some regular interval), reset everything (stub, channel, stream) to nullptr to start afresh. This is required because at times Write(...) does not fail even if there is no connection between the client and the service. Write(...) calls are successful but the outgoing port on the client does not show any activity on wireshark!
Here is the code:
constexpr GRPC_TIMEOUT_S = 10;
constexpr RESTART_INTERVAL_M = 15;
constexpr GRPC_KEEPALIVE_TIME_MS = 10000;
string root_ca, tls_key, tls_cert; // for SSL
string remote_addr = "https://remote.com:5445";
...
...
void ResetStreaming() {
if (stub_p) {
if (strm_p) { // graceful restart/stop, this pair of API are called together, in this order
if (!strm_p->WritesDone()) {
// Log a message
}
strm_p->Finish(); // Log if return value of this is NOT grpc::OK
}
strm_p = nullptr;
strm_ctxt_p = nullptr;
stub_p = nullptr;
channel_p = nullptr;
}
}
void CreateStub() {
if (!stub_p) {
ChannelArguments chan_args;
chan_args.SetInt(GRPC_ARG_KEEPALIVE_TIME_MS, GRPC_KEEPALIVE_TIME_MS);
channel_p = CreateCustomChannel(
remote_addr,
SslCredentials(SslCredentialsOptions{root_ca, tls_key, tls_cert}),
chan_args);
stub_p = GrpcService::NewStub(m_channel_p);
}
}
void Stream() {
const auto restart_time = steady_clock::now() + minutes(RESTART_INTERVAL_M);
while (!stop) {
// restart every RESTART_INTERVAL_M (15m) even if ALL IS WELL!!
if (steady_clock::now() > restart_time) {
break;
}
Update updt = GetUpdate(); // get the update to be sent
if (!stop) {
if (channel_p->GetState(true) == GRPC_CHANNEL_SHUTDOWN ||
!strm_p->Write(updt)) {
// could not write!!
return; // we will Connect() again
}
}
}
// stopped due to stop = true or interval to create new stream has expired
ResetStreaming(); // channel, stub, stream are recreated once in every 15m
}
bool PingRemote() {
ClientContext ctxt;
ctxt.set_deadline(system_clock::now() + seconds(GRPC_TIMEOUT_S));
Empty req, resp;
CreateStub();
if (stub_p->Ping(&ctxt, req, &resp).ok()) {
static UpdateAck ack;
strm_ctxt_p = make_unique<ClientContext>(); // need new context
strm_p = stub_p->PushUpdate(strm_ctxt_p.get(), &ack);
return true;
}
if (strm_p) {
strm_p = nullptr;
strm_ctxt_p = nullptr;
}
return false;
}
void Connect() {
while (!stop) {
if (PingRemote() || stop) {
break;
}
sleep_for(seconds(5)); // wait before retrying
}
}
// set to true from another thread when we want to stop
atomic<bool> stop = false;
void StreamUntilStopped() {
if (stop) {
return;
}
strm_thread_p = make_unique<thread>([&] {
while (!stop) {
Connect();
Stream();
}
});
}
// called by the thread that sets stop = true
void Finish() {
strm_thread_p->join();
}
With this we are seeing that the streaming recovers within 15 minutes (or RESTART_INTERVAL_M) whenever there is a disruption for any reason. This code runs in a fast path, so I am curious to know if this can be made any better.

GUI freeze when inserting javascript into a WebView in Qt

I've got a small chat application that uses a QTcpSocket to communication with my server.
My server sends JSON commands to my Qt chat client which i parse inside of the readyRead(). When a receive the command newUser the below method is executed. For some reason the below method causes my application to freeze on some machines. Meaning it yields the GUI to become unresponsive. The user then has to minimize and then maximize the application for it to become responsive.
Any ideas what could be causing this?
void MainWindow::addUser(QString channelID, QString visitorName) {
int exists = 0;
if (onlineUsers.contains(channelID)) {
exists = 1;
}
// Add the user to our QWebView
ui->allVisitorsWeb->page()->mainFrame()->evaluateJavaScript("$('#allUsers tr:last').after('<tr id=" + channelID + "><td>" + visitorName + "</td></tr>'); undefined");
// Add to active chats QStringList
onlineUsers << channelID;
// Display notification
QSettings settings("MyApp", "App");
settings.beginGroup("aSettings");
if (settings.value("visitorNotification").toString() == "true") {
//displayNotification(visitorName, "Has entered", channelID);
}
settings.endGroup();
// Added to fix freeze
QCoreApplication::processEvents();
}

A connection is made between server and client but unable to send data via OutputStream in J2ME

I started to program client/server applications in J2ME recently.Now I'm working with c++ builder 2010 indy components (e.g. TidTTCPServer) and J2ME. My application is designed to restart the kerio winroute firewall service from a remote machine.
My server application is written in c++ builder 2010, I've put a TidTCTServer component into a form which binded to 127.0.0.1:4500. That's listening on port 4500 in local machine.
Then i've added a listbox that i need to add every upcoming packets converted to UnicodeString.
//void __fastcall TForm1::servExecute(TIdContext *AContext)
UnicodeString s;
UnicodeString txt;
txt=Trim(AContext->Connection->IOHandler->ReadLn());
otvet->Items->Add(txt);
otvet->ItemIndex=otvet->Items->Count-1;
if (txt=="1") {
AContext->Connection->IOHandler->WriteLn("Suhrob");
AContext->Connection->Disconnect();
}
if (txt=="2") {
AContext->Connection->IOHandler->WriteLn("Shodi");
AContext->Connection->Disconnect();
}
//----------------------------------------------------------------------------
// void __fastcall TForm1::servConnect(TIdContext *AContext)
++counter;
status->Panels->Items[0]->Text="Connections:" + IntToStr(counter);
status->Panels->Items[1]->Text="Connected to " + AContext->Connection->Socket->Binding->PeerIP + ":" + AContext->Connection->Socket->Binding->PeerPort;
and my client side code looks smth like this:
else if (command == send) {
// write pre-action user code here
InputStream is=null;
OutputStream os=null;
SocketConnection client=null;
ServerSocketConnection server=null;
try {
server = (ServerSocketConnection) Connector.open("socket://"+IP.getString()+":"+PORT.getString());
// wait for a connection
client = (SocketConnection) Connector.open("socket://"+IP.getString()+":"+PORT.getString());
// set application-specific options on the socket. Call setSocketOption to set other options
client.setSocketOption(SocketConnection.DELAY, 0);
client.setSocketOption(SocketConnection.KEEPALIVE, 0);
is = client.openInputStream();
os = client.openOutputStream();
// send something to server
os.write("texttosend".getBytes());
// read server response
int c = 0;
while((c = is.read()) != -1) {
// do something with the response
System.out.println((char)c);
}
// close streams and connection
}
catch( ConnectionNotFoundException error )
{
Alert alert = new Alert(
"Error", "Not responding!", null, null);
alert.setTimeout(Alert.FOREVER);
alert.setType(AlertType.ERROR);
switchDisplayable(alert, list);
}
catch (IOException e)
{
Alert alert = new Alert("ERror", e.toString(), null, null);
alert.setTimeout(Alert.FOREVER);
alert.setType(AlertType.ERROR);
switchDisplayable(alert, list);
e.printStackTrace();
}
finally {
if (is != null) {
try {
is.close();
} catch (Exception ex) {
System.out.println("Failed to close is!");
}
try {
os.close();
} catch (Exception ex) {
System.out.println("Failed to close os!");
}
}
if (server != null) {
try {
server.close();
} catch (Exception ex) {
System.out.println("Failed to close server!");
}
}
if (client != null) {
try {
client.close();
} catch (Exception ex) {
System.out.println("Failed to close client!");
}
}
}
my client application gets connected with the server but when i try to send data such as
os.write("texttosend".getBytes());
I cannot get text data on the server using. That's I am not getting sent packets in the server from client.
txt=Trim(AContext->Connection->IOHandler->ReadLn());
Guys, where am I wrong? is the way i'm doing is ok?
Or do I need to use StreamConnection instead of SocketConnection?
And when i use telnet to send data it works cool, strings will be added to listbox
telnet 127.0.0.1 4500
texttosend
23
asf
Any help is appreciated !!!
Thanks in advance!
The main problem is that you are using ReadLn() on the server end. ReadLn() does not exit until a data terminator is encountered (a LF line break character is the default terminator) or if a reading timeout occurs (Indy uses infinite timeouts by default). Your J2ME code is not sending any data terminator, so there is nothing to tell ReadLn() when to stop reading. The reason it works with Telnet is because it does send line break characters.
The other problem with your code is that TIdTCPServer is a multi-threaded component, but your code is updating the UI components in a thread-unsafe manner. You MUST synchronize with the main thread, such as by using Indy's TIdSync and/or TIdNotify classes, in order to update your UI safely from inside of the server's event handlers.
Yes, flush method is necessary to call after sending bytes, but ..... finally....
then i tried to include my connection code in a new thread that implements Runnable worked perfectly. Now I've found where I was wrong!!!!!!!!!!!!!!
That's guys you need to include above code in the following block.
Thread t= new Thread(this);
t.start();
public void run()
{
//here paste the code
}
Try OutputStream.flush()?
If not, try writing to a known working server, instead of one you've created yourself (something like writing "HELO" to an SMTP server), this will help you figure out which end the error is at.