Debugging Game AI

posted in trill41 for project ABx
Published February 18, 2020
AI
Advertisement

Some time ago I made a Behavior tree implementation for this game. While the implementation seems to work, the NPCs sometimes behave very silly, so I thought it would clarify their behavior when I could look into their “head”. Classical debugging a game or game server is always a pain. When everything is executed 60 or 20 times per second it's hard to step through and inspect variables. This is even more difficult with a game server which may disconnect the client when you don't inspect the variables fast enough, because of some timeout.

IPC

I solved this problem with writing an IPC server that sends the status of the behavior tree to a connected IPC client (debug client), which displays the status. That's not real debugging, because I can only inspect the status and not pause or step through it. It communicates over TCP sockets using asio.

The IPC library is really simple and can send anything (i.e. struct or class) from server to client and vice versa. Server and client communicate with so called “Messages” which are simple structs (or even classes) which just need a public Serialize() function:

struct MyMessage
{
    int intValue;
    std::string strValue;
    template<typename _Ar>
    void Serialize(_Ar& ar)
    {
        ar.value(intValue);
        ar.value(strValue);
    }
};

It can even have variable sized containers:

struct MyMessage3
{
    struct Item
    {
        uint16_t type;
        uint32_t index;
        uint8_t place;
        std::string name;
    };

    uint16_t count;
    std::vector<Item> items;
    template<typename _Ar>
    void Serialize(_Ar& ar)
    {
        ar.value(count);
        items.resize(count);
        for (uint16_t i = 0; i < count; ++i)
        {
            auto& item = items[i];
            ar.value(item.type);
            ar.value(item.index);
            ar.value(item.place);
            ar.value(item.name);
        }
    }
};

Starting the Server

Starting a server is just a few lines of code and it can reuse an existing asio::io_service:

#include "IpcServer.h"

asio::io_service io;
asio::ip::tcp::endpoint endpoint(asio::ip::address(asio::ip::address_v4(INADDR_ANY)), 1234);
IPC::Server server(io, endpoint);
io.run();

Client connecting to server

#include "IpcClient.h"

asio::io_service io;
IPC::Client client(io);
client.Connect("localhost", 1234);
io.run();
// Or io.poll() or whatever fits the application.

Sending messages

The client and server can send a message with a proper Serialize() to each other with the Send() or SendTo() function:

// Client to server
MyMessage msg { 42, "Hello friends" };
client_.Send(msg);
MyMessage msg { 42, "Hello friends" };
// Server sends the message to all connected clients
server.Send(msg);
// Server sends the message to a specific client
server.SendTo(client, msg);

Handling messages

When the client or server get some message they can set an message handler:

// Client
handlers_.Add<MyMessage>([](const MyMessage& msg)
{
    std::cout << "intValue = " << msg.intValue << std::endl;
    std::cout << "strValue = " << msg.strValue << std::endl;
});
// Server
handlers_.Add<MyMessage>([](IPC::ServerConnection& client, const MyMessage& msg)
{
    std::cout << "intValue = " << msg.intValue << std::endl;
    std::cout << "strValue = " << msg.strValue << std::endl;
});

Debug client

The client is a terminal application using ncurses on Linux and PDCurses on Windows, but it doesn't work well on Windows, because of some limitations of the Windows' terminal. Anyway, since the IPC works over TCP sockets, I can just connect from a Linux running the Debug client to a Windows running the game server.

Debug client running on Linux

The client has three windows:

  1. Game selector. Because one game server can run many games, this is for selecting the game to inspect.
  2. NPC selector to select the NPC to inspect
  3. NPC details, which shows some basic stats and the status of the behavior tree in real time.

Conclusion

At the moment I didn't find any obvious reasons why the NPCs act silly, but this gives me the tools to investigate it further.

Previous Entry Linux and Open Source
1 likes 5 comments

Comments

jbadams

Clever approach, hopefully you can pinpoint your AI problems! ?

February 20, 2020 12:04 AM
trill41

Thank you. But the idea with a server for inspecting the state was not mine, I took it from SimpleAI.

I think this kind of stuff needs a lot fine tuning and hopefully this tool gives me the information I need for it.

February 20, 2020 06:04 PM
jbadams

For sure - do you also log the data? Detailed logs (especially if you can add some formatting or filtering to find the information you need) can be invaluable for tracking down issues.

February 21, 2020 03:28 AM
trill41

At the moment I do not log the data, but this is a good idea. That wouldn't be too hard, now that it is possible to get the data, it could also be stored, not just displayed. For sure this would be very useful, instead of sitting in front of a terminal and waiting for something interesting to happen, it would be nicer to search and filter the interesting parts. And, because you can only inspect one NPC at a time, all the other information is lost now. This wouldn't be the case when all the information is stored in a file.

I think I should really do this, thank you.

February 22, 2020 06:38 AM
You must log in to join the conversation.
Don't have a GameDev.net account? Sign up!
Profile
Author
Advertisement

Latest Entries

Advertisement