FLWL / aoe2-ai-module

Library to extend AI scripting capabilities in Age of Empires 2.
GNU Lesser General Public License v2.1
16 stars 6 forks source link

grpc not connecting? #7

Open Overconfidence opened 3 years ago

Overconfidence commented 3 years ago

Hi, I'm new to this grpc, proto stuff, so I don't know how difficult or easy my problem is, but here it is. I tried to test the client_python file to see if I can get data out of the game (or from a recorded game), when I encountered this bug, not sure what causes it / how to solve it: image

My steam was in offline state, and I had a singleplayer skirmish playing in DE with 2 bots + me. I think it happens before the 5 second timeout.

FLWL commented 3 years ago

This error happens when the grpc server isn't running, for example when the AI Module DLL has not been loaded into the game process. You should see a command line window saying "Debug console initialized" when the module is successfully loaded and started. Have you done so? If so, how?

There are plans to include a Python script in this repository for easily loading the DLL. However, the module is still in an early development phase and as such it hasn't been done yet.

Overconfidence commented 3 years ago

Sorry, for the late reply, as a uni student I'm flooded with projects, and it's difficult to work on everything at once. This is a first time I use (and import) DLL, so I don't know if I'm doing it right. After reading your answer I loaded the DLL into python with the following lines (I copied the dll next to the .py file): image This gave me the followin result (error): image Right now I'm trying to figure out, if I imported/loaded the DLL right, and where else do I have to change the client_python.py file. Thank you for spending time helping a beginner.

Overconfidence commented 3 years ago

AI Module DLL has not been loaded into the game process.

Do you mean as injecting the dll into the game process? If so, I have no idea how that works, even after reading about it.

FLWL commented 3 years ago

AI Module DLL has not been loaded into the game process.

Do you mean as injecting the dll into the game process? If so, I have no idea how that works, even after reading about it.

Yes. I'm happy to help, but I have to say that the module is not beginner friendly yet. I've made the code available early so that people with the know-how could contribute.

https://github.com/FLWL/aoc-auto-game/blob/master/example_scripts/launch_aoc.py this is from one of my AoC projects and shows how loading is done for 32-bit processes. But DE is 64-bit, so it has to be ported, mainly the ctypes calls and arguments. Its on the to-do list. Of course you could also find a working example off the internet, but I can't vouch for those.

Overconfidence commented 3 years ago

Thank you, I will try to wrap my head around this thing then.

PS: How can I see the structure of the dll somehow? visual studio doesn't really want to show me it.

FLWL commented 3 years ago

Thank you, I will try to wrap my head around this thing then.

PS: How can I see the structure of the dll somehow? visual studio doesn't really want to show me it.

By structure do you mean the functions that are callable via grpc? Or how the C++ classes in the module are laid out?

Overconfidence commented 3 years ago

the functions

FLWL commented 3 years ago

At the high level they are defined in *.proto files in the protos folder and its subdirectories. I'd look at those. From there they'll get compiled by protoc to C++ and Python include files that are actually used in the code. There is also some automatically generated documentation at the repository's Wiki.

Overconfidence commented 3 years ago

Hi, I did a bit of diggin around the injection, and I tried to inject the dll into the game's process, but got the same error as the first time (first picture shows the same error message). I tried to inject with the following c++ code: `#include

include

include

DWORD GetProcId(const char* procName) { DWORD procId = 0; HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);

if (hSnap != INVALID_HANDLE_VALUE)
{
    PROCESSENTRY32 procEntry;
    procEntry.dwSize = sizeof(procEntry);

    if (Process32First(hSnap, &procEntry))
    {
        do
        {
            if (!_stricmp(procEntry.szExeFile, procName))
            {
                procId = procEntry.th32ProcessID;
                break;
            }
        } while (Process32Next(hSnap, &procEntry));
    }
}
CloseHandle(hSnap);
return procId;

}

int main() { const char dllPath = "E:\egyebek\PyCharm\Projects\AoE2De_Testing_Ground\aoe2-ai-module\aimodule\x64\Release\aimodule.dll"; const char procName = "AoE2DE_s.exe"; DWORD procId = 0;

while (!procId)
{
    procId = GetProcId(procName);
    Sleep(30);
}

HANDLE hProc = OpenProcess(PROCESS_ALL_ACCESS, 0, procId);

if (hProc && hProc != INVALID_HANDLE_VALUE)
{
    void* loc = VirtualAllocEx(hProc, 0, MAX_PATH, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
    if (loc)
    {
        WriteProcessMemory(hProc, loc, dllPath, strlen(dllPath) + 1, 0);
    }
    HANDLE hThread = CreateRemoteThread(hProc, 0, 0, (LPTHREAD_START_ROUTINE)LoadLibraryA, loc, 0, 0);

    if (hThread)
    {
        CloseHandle(hThread);
    }
}
if (hProc)
{
    CloseHandle(hProc);
}
std::cout << "Injection Finished!";
return 0;

}`

FLWL commented 3 years ago

I think the issue in your code is on this line: const char* dllPath = "E:\egyebek\PyCharm\Projects\AoE2De_Testing_Ground\aoe2-ai-module\aimodule\x64\Release\aimodule.dll"; The single backwards slash you are using as a directory separator introduces a bunch of unwanted escape characters such as \e or \x. You should escape the slash itself like this: const char* dllPath = "E:\\egyebek\\PyCharm\\Projects\\AoE2De_Testing_Ground\\aoe2-ai-module\\aimodule\\x64\\Release\\aimodule.dll";

Before connecting with gRPC, make sure that the injection was successful first. You should see a console window appear with text "Debug console initialized."

Also note that DE just had a large update 42848 and this project is not yet updated to work with that build. It could crash or result in other undefined behavior. I'll update it soon.

Overconfidence commented 3 years ago

Double checked the line you mentioned, it does have the double slash, for some reason it wasn't copied over correctly to my previous comment.

I get the standard console window, which pops up, when you run code in VS, but it doesn't show the "Debug console initialized." message, and neither do I get another console window showing the message.

I'm in offline mod on steam for the past few days (or weeks now), so the update didn't get trough to my DE.

FLWL commented 3 years ago

Your code worked fine for me. If #define DEBUG_MODE is set in misc/Configuration.h (which it is by default), the console window will be created by the module from inside the game process upon loading: AoE2DE_s_vB2M6i0vKJL

Overconfidence commented 3 years ago

The #define DEBUG_MODE is set in the Configuration.h file (I didn't change it): #ifdef DEBUG_MODE static const int64_t ADDR_FUNC_DEF_ACTION = 0x7FF7D1075C30; static const int64_t ADDR_FUNC_DEF_FACT = 0x7FF7D1076100;

However... reading your comment about this Configuration.h file, and thinking why it doesn't work for me, I came to a question regarding my problem. Does it matter where the injector program is located on the computer, or can it be anywhere? Same question goes for the dll file. (I remade my injection program next to the misc folder, and included it in my program, hoping it would help, but it did not.) I also set the execution level to administrator in project->properties->linker->manifest file->UAC Execution level.

Overconfidence commented 3 years ago

Hi, managed to get the dll injected and got the Debug console pop up, however I got an error message when running the client_python program saying status = StatusCode.CANCELLED: image Is this because of the DE update, or something else I'm unaware of?

FLWL commented 3 years ago

To answer the question I missed before: it should not matter where the injector or the DLL file is located, with possibly some exceptions such as very long path names or unusual symbols.

Looks like you got the DLL working, as you are receiving a custom error message sent from the DLL via the RPC server. If possible, you should also mention what caused the problem, as it might help someone else with a similar issue.

Now the "Request was not processed in time by the game thread." response can happen when a match is not running (or is paused) or the game and AI Module versions mismatch. The latter seems likely, as the dev release currently supports DE build 42848, which is not the latest. I've been focusing more on finalizing AoC support lately and holding off on DE, as there are some fundamental problems to solve with the latter. An update is in the works though.

Overconfidence commented 3 years ago

In my opinion the problem is from the DE update version mismatch, I just wanted to make sure, that I didn't overlook something.

Overconfidence commented 3 years ago

Hi, managed to de-patch DE to the previous patch, and the dll works now. Can this program be used with replay files, and can it read human player's data? Tried to check these things with client_python.py but I got errors. I also tried to give the computer controlled player commands, but I failed to understand to how to do it correctly. I hope you can shed some lights on these things.

FLWL commented 3 years ago

Hi, as it is now this program is of not much use with replays. It can only issue actions and facts to AI players in live single player matches. Replay files are a set of static recorded events, which cannot be changed or analyzed with this method. If you have an ongoing match with a human and an AI, you can see the human data from the AI perspective by using facts such as fact.CcPlayersBuildingCount(playerNumber=1) as it is in the Python client.

The goal of this program is to augment in-game AI agents with external means such as machine learning for research purposes. To do so, you would start a single player match with an AI player in at least one slot. I didn't want the in-game AI interfering with my commands so I created a new dummy .ai and .per files just for this. Now if the AI module is loaded, it will accept commands over the RPC server on each in-game AI tick. So the match has to be actively running for the commands to work - otherwise you will get a timeout error. You also have to specify the player slot number you want to issue the commands for. By default it is slot 1 in the Python client example (first argument for pack_commands). In that case if there isn't an AI in slot 1 you will also get a timeout error because the AI tick will never be executed for player 1. If all goes well the module will reply with a list of results, one for each sent command. The whole process is in the Python client example. In case of problems, specific error messages would help to narrow down the issue.

About module updates - the next version is pretty much ready to go and the code already in the dev branch. This one includes a lot of refactoring. As soon as I'll get GitHub Actions build scripts fixed again, the binaries will be available as well.