Gamemode SDK for C/C++ (GDK)
#61

Quote:
Originally Posted by kurta999
Посмотреть сообщение
Any idea how to import the "command processor" into C++, example ZCMD?

I think its possible to do with "int asdasd(...)" but I don't know how to call it.
you can use a std::map with pointers to functions.

http://www.newty.de/fpt/index.html

http://stackoverflow.com/questions/1...ist-at-runtime
Reply
#62

Thanks!!

Anyway possible to do something like this without initialazation?
But this is not problem for me.
Reply
#63

Quote:
Originally Posted by kurta999
Посмотреть сообщение
Thanks!!

Anyway possible to do something like this without initialazation?
But this is not problem for me.
nope but with a custom preprocessing step youcould make the initializationlistgeneration automatic. but I'm not going in depth on that.figure it out yourself.
Reply
#64

Okey, and it won't be slow if I use ~1500cmd with this method?
Reply
#65

Quote:
Originally Posted by kurta999
Посмотреть сообщение
Okey, and it won't be slow if I use ~1500cmd with this method?
if a normal programwhich has thousands of function isn't slow, why should this be?
But ye, about the map: the more you store the slower the lookup.

The lookup time is O(Log(n)) with n the amount of items in the container.

To make it easier, let's say we have 100 commands in the list, this is our base for now. The lookup time will be 2.
if we have 10000 commands in the list, the lookup time will be 4. which is just 2 times slower than when we have 100 commands in the map.

Here would be a theoretical graph of access times(y) according to the amount of commands(x):

[Image: uc?id=0BzUKBMbvP0EFdm9UWTc1UUZBQnc&export=view]

for an unordered map, if you hashing function is good, the access time will be O(1) [so always be the same], however - worst case O(n) [proportional to the amount of items in the container]. In an unordered map you trade size for speed so beware of the memory hit.
Reply
#66

Quote:
Originally Posted by Gamer_Z
Посмотреть сообщение
if a normal programwhich has thousands of function isn't slow, why should this be?
But ye, about the map: the more you store the slower the lookup.

The lookup time is Log(n) with n the amount of items in the container.

To make it easier, let's say we have 100 commands in the list, this is our base for now. The lookup time will be 2.
if we have 10000 commands in the list, the lookup time will be 4. which is just 2 times slower than when we have 100 commands in the map.
Okey, then this is ~3ms. This is not problem, thanks.
Reply
#67

Function time complexity is not associated with the actual time it may take for it to execute as this may greatly vary from system to system. Instead it represents the number of operations performed.

The complexity of map::find is O(log n) - that is very efficient and comes from how maps operate internally using binary search.

Anyways this is a very elegant solution in terms of code readability and file organizing, but lets not get ourselves too deep into the premature optimization, shall we :P
Reply
#68

Quote:
Originally Posted by AndreT
Посмотреть сообщение
Function time complexity is not associated with the actual time it may take for it to execute as this may greatly vary from system to system. Instead it represents the number of operations performed.

The complexity of map::find is O(log n) - that is very efficient and comes from how maps operate internally using binary search.

Anyways this is a very elegant solution in terms of code readability and file organizing, but lets not get ourselves too deep into the premature optimization, shall we :P
I'm working on a library for SampGDK which will provide ZCMD:command , just like in PAWN.

Edit: I made some progress.. this is what I currently have made:
pawn Код:
#include <iostream>
#include <map>
#include <algorithm>
#include <string>

#define PLUGIN_EXPORT
#define PLUGIN_CALL

//begin command library
//header
class Command
{
public:
   virtual bool do_command(int playerid, std::string params) = 0;
};

#define CMD(name) \
class cmd ## name : public Command\
    {\
        public:\
        cmd ## name() { ZCMD_COMMAND_LIBRARY_register_command(this, "/"#name); }\
        bool do_command(int playerid, std::string params)
//M$ and clang have a mangled name limit of 2048 chars, g++ "unlimited" (well, limited by memory).
#define CMDEND(name) };cmd ## name ZCMD_COMMAND_LIBRARY_CMD_ ## name;

bool OnPlayerCommandReceived(int playerid,std::string command, std::string params);
void OnPlayerCommandExecuted(int playerid, std::string, std::string params, bool success);

//source
std::map<std::string, Command*> ZCMD_COMMAND_LIBRARY_command_map;
void ZCMD_COMMAND_LIBRARY_register_command(Command* cmd, std::string name)
{
    std::string data(name);
    std::transform(data.begin(), data.end(), data.begin(), ::tolower);
    ZCMD_COMMAND_LIBRARY_command_map[data] = cmd;
}

PLUGIN_EXPORT bool PLUGIN_CALL OnPlayerCommandText(int playerid, const char *cmdtext)
{
    std::string main(cmdtext);
    std::string command;
    std::string parameters;

    size_t space = main.find(' ');

    if(space == std::string::npos)
    {
        command.assign(main);
    }
    else
    {
        command.assign(main.begin(),main.begin()+space);
        parameters.assign(main.begin()+(space+1),main.end());
    }

    std::transform(command.begin(), command.end(), command.begin(), ::tolower);

    if(OnPlayerCommandReceived(playerid,command,parameters))
    {
        if(ZCMD_COMMAND_LIBRARY_command_map.find(command) != ZCMD_COMMAND_LIBRARY_command_map.end())
        {
            bool success = ZCMD_COMMAND_LIBRARY_command_map[command]->do_command(playerid,parameters);
            OnPlayerCommandExecuted(playerid, command, parameters, success);
            return success;
        }
    }
    else
    {
        return false;
    }
    //printf("Unknown command: %s\n",command.c_str());
    return false;
}
//end command library

//end-user code

bool OnPlayerCommandReceived(int playerid,std::string command, std::string params)
{
    return true;
}

void OnPlayerCommandExecuted(int playerid, std::string command, std::string params, bool success)
{
    return;
}

CMD(start)
{
    //printf("start issued, params: '%s'\n",params.c_str());
    return true;
}
CMDEND(start);

CMD(end)
{
    //printf("end issued, params: '%s'\n",params.c_str());
    return false;
}
CMDEND(end);

//end commands

//test run
#include <Windows.h>
int main()
{
    DWORD end;
    DWORD time = GetTickCount();
    for(int i =0; i < 1000000; ++i)
    {
        OnPlayerCommandText(0, "/start");
        OnPlayerCommandText(0, "/StaRT");
        OnPlayerCommandText(0, "/start ello lololol");
        OnPlayerCommandText(0, "/kill");
        OnPlayerCommandText(0, "/end");
        OnPlayerCommandText(0, "/end 1234");
    }
    end = GetTickCount();
    printf("%d ms\n",end-time);
    return 0;
}
it takes 2 seconds to execute on my machine. That means it can process a total of around 500,000 commands in one second.

Edit2:
Done: https://sampforum.blast.hk/showthread.php?tid=436322
Reply
#69

Quote:
Originally Posted by xeeZ
Посмотреть сообщение
Calling other plugins' natives is a bit complex: you have to have an AMX instance on which you call the native function and do all the argument conversion (pushing and popping them on to the stack) similar to public functions.

You can use sampgdk_get_natives() and sampgdk_num_natives() to get the address of the native function you want to call. The former returns a pointer to the array of *all* native functions that have been registered (including those provided by plugins), not just the ones that are used by the AMX - so it's basically unimportant which AMX you'll pass in. The latter returns the number of entries in the array.

For example, to call SendClientMessage() you'd do something like this:

pawn Код:
AMX_NATIVE FindNative(const char *name) {
    const AMX_NATIVE_INFO *natives = sampgdk_get_natives();
    int num_natives = sampgdk_num_natives();

    for (int i = 0; i < num_natives; i++) {
        if (strcmp(natives[i].name, name) == 0) {
            return natives[i].func;
        }
    }

    return 0;
}

bool DoSendClientMessage(int playerid, int color, const char *message) {
    // Make this variable static to search for SendClientMessage only once.
    static AMX_NATIVE native = FindNative("SendClientMessage");

    // Push the arguments on to the stack. Here "amx" is some variable that
    // holds a pointer to some AMX instance.
    cell message_addr;
    amx_PushString(amx, &message_addr, 0, message, false, false);
    amx_Push(amx, color);
    amx_Push(amx, playerid);
    amx_Push(amx, 3 * sizeof(cell)); // 3 is the number of arguments

    // All the "push" functions increase the parameter count which is later
    // used (and reset) by amx_Exec(). But we don't use amx_Exec() so it has to
    // be reset manually.
    amx->paramcount = 0;

    // Get the "params" pointer which points to our newly pushed arguments.
    cell *params;
    amx_GetAddr(amx, amx->stk, &params);

    // Call the function and pop the arguments off the stack.
    cell retval = native(amx, params);
    amx->stk += params[0];

    // Release all the string arguments as you would do with publics.
    amx_Release(amx, message_addr);

    return retval;
}
Note that some plugins like MySQL are built on top of third-party C or C++ libraries which you can use instead (and they are likely to be more flexible).
The amx is undefined on line :
amx_PushString(amx, &message_addr, 0, message, false, false);

How to initialize and define it ?
Also another question is that do I have to modify sampgdk code to call the plugins functions or I can simply use the code on my C++ gamemode ?
Reply
#70

Quote:
Originally Posted by LeaveMe
Посмотреть сообщение
The amx is undefined on line :
amx_PushString(amx, &message_addr, 0, message, false, false);

How to initialize and define it ?
You can actually add
Код:
AMX *amx
into the list of arguments of
Код:
bool DoSendClientMessage()
.
Actually it should look like this:
Код:
bool DoSendClientMessage(AMX *amx, int playerid, int color, const char *message)
Do this to keep track of from what AMX it's being used.

Also use the AMX pointer of any valid AMX source, where you actually use DoSendClientMessage().
Reply
#71

LeaveMe, the code up there does this:
amx->stk += 11;
whereas it should do this:
amx->stk += 12;

You have to account for the stack space used to allocated the last amx_Push call, so do
amx->stk += (12 * sizeof(cell));
Reply
#72

Thank you AndreT but I guess you are wrong or Zeex example is wrong.

Can you take a look at Zeex example ?
http://forum.sa-mp.com/showpost.php?...7&postcount=36

It's coded as amx->stk += params[0];

Please let me know id you found out anything else.
Reply
#73

I contacted Zeex about this back in April:
Quote:
Originally Posted by xeeZ
Looks like you are right - amx->stk indeed should be incremented by "params_size + sizeof(cell)".
The issue here is quite important depending on how frequently your C++ plugin calls the native hooking function.

I think I'll privmsg him since people do still use that post for reference! Thanks for reminding.
Reply
#74

Thank you very very much AndreT!!!
That's iit.
I used to call the plugin function million times and it crashed the server.
Then replaced ' amx->stk += params[0];' with ' amx->stk += params[0] + sizeof(cell);'. Now everything seems to be fine.
Thank you again.
Reply
#75

Hey, many thanks to xeeZ for this awesome plugin.
Theres just one problem, which i can't find a solution for. How do I send german umlauts like д, ц, ь to players?
In pawn I always used escape codes like \228; \246; \252;, but this does not work with sampgdk. Directly writing the umlauts doesn't work either.

Thanks,
Blowfish
Reply
#76

nice
Reply
#77

Quote:
Originally Posted by Blowfish
Посмотреть сообщение
Hey, many thanks to xeeZ for this awesome plugin.
Theres just one problem, which i can't find a solution for. How do I send german umlauts like д, ц, ь to players?
In pawn I always used escape codes like \228; \246; \252;, but this does not work with sampgdk. Directly writing the umlauts doesn't work either.

Thanks,
Blowfish
I don't think that this is the fault of sampgdk. You probably just have to change your character set in your project settings (if you use Visual Studio). If you are looking for a way to translate the german umlauts from one character set to another with coding, take a look at the CTeamspeak.cpp from my Teamspeak-Connector plugin.
Reply
#78

Of course it's not the fault of sampgdk, but i couldn't find a solution, so I thought this was the best place to ask. I don't use any IDE, just a text editor and makefiles.
Thank's for the hint, i'm going to take a look at the Teamspeak thingy right away.
Reply
#79

How to set it up on Windows? I've including the include directory and added the .lib to the library settings but it doesn't work when using this example: https://github.com/Zeex/sampgdk/blob...helloworld.cpp

error C2146: syntax error : missing ';' before identifier 'RepeatingTimer'
error C2182: 'SAMPGDK_TIMER_CALL' : illegal use of type 'void'
Reply
#80

Replace SAMPGDK_TIMER_CALL with SAMPGDK_CALL.
Reply


Forum Jump:


Users browsing this thread: 7 Guest(s)