[Include] botsync - Communicating between NPCs and the server
#1

botsync

I wasn't sure whether reposting this would still be useful (topic originated from 2009) but I guess some people might find it useful.
Introduction

Now hopefully I've not missed something here and there is a dead easy way of doing this - but as far as I can see you can't send messages between NPCs and regular scripts (if there is an existing way please tell me). Some callbacks aren't available in NPC scripts, but what if you want the bot to do something which can only be done from an NPC script when those callbacks, or other events, occur? This seemed like a slight shortcoming to me so I decided to write a cross-process communication script - "botsync".

Setup

Botsync, for simplicity reasons, consists of two parts - a filterscript you need to have running all the time and an include you need to include into any gamemode, filterscript or npcscript which you want to include cross-process communication in. Note that botsync requires the latest version of foreach.
  • Gamemodes
Код:
#include <a_samp>
#include <botsync>

main () {}

public
    OnBotSyncData(bot, const name[], const msg[])
{
    return 1;
}
The "OnBotSyncData" callback is not required, it is just the callback which is called when data is recieved, you only need it in any given script if that script is going to recieve data.
  • Filterscripts
Код:
#define FILTERSCRIPT

#include <a_samp>
#include <botsync>

public
    OnBotSyncData(bot, const name[], const msg[])
{
    return 1;
}
  • NPCScripts
Код:
#define BOT_SYNC_CALLBACK OnBotSyncData

#include <a_npc>
#include <botsync>

main () {}

public
    OnBotSyncData(bot, const name[], const msg[])
{
    return 1;
}

public
    OnNPCConnect(myplayerid)
{
    SetTimer("RemoteCall", 1000, 1);
}

public
    OnClientMessage(color, text[])
{
    return 0;
}
Note that, as NPCScripts don't currently have CallLocalFunction, you MUST include OnNPCConnect and OnClientMessage. You must also include the "BOT_SYNC_CALLBACK" define above the includes if you want to use "OnBotSyncData".

Use

There are two main functions in this include:
  • BotSync_Send(bot, name[], msg[]);

    This function sends a message to a specified bot or, if called from a bot, the main server. If you use this function in an NPCScript the "bot" parameter is always "-1" (there may be a future expansion where you can send messages from bot to bot, but for now it's only "-1" for the server).
  • BotSync_SendFast(bot, name[], msg[]);

    This function can ONLY be used in GameModes and FilterScripts, you can not use it to send messages from NPCScripts to the server. If you are doing one way communication this function is far better. Bots do not know from which function the data was sent, they both call OnBotSyncData. You can still reply to fast messages in bots, but only using the slower BotSync_Send function.
Examples
  • Random 2-way comms
GM:

Код:
public
    OnBotSyncData(bot, const name[], const msg[])
{
    BotSync_Send(bot, "thanks", "got your message");
    return 1;
}
NPC:

Код:
forward
    RemoteCall();

public
    RemoteCall()
{
    BotSync_Send(-1, "hello", "hi from a bot");
}

public
    OnBotSyncData(bot, const name[], const msg[])
{
    new
        File:f = fopen("debugout.txt", io_append);
    if (f)
    {
        fwrite(f, "input from server\n");
        fwrite(f, name);
        fwrite(f, "\n");
        fwrite(f, msg);
        fwrite(f, "\n");
        fclose(f);
    }
    return 1;
}

public
    OnNPCConnect(myplayerid)
{
    SetTimer("RemoteCall", 1000, 1);
}
  • Additional Callbacks
FS:

Код:
public
    OnPlayerEnterVehicle(playerid, vehicleid, ispassenger)
{
    new
        msg[16];
    format(msg, sizeof (msg), "%d %d %d", playerid, vehicleid, ispassenger);
    BotSync_SendFast(bot, "OnPlayerEnterVehicle", msg);
    return 1;
}
NPC:

Код:
public
    OnBotSyncData(bot, const name[], const msg[])
{
    if (!strcmp(name, "OnPlayerEnterVehicle"))
    {
        new
            playerid,
            vehicleid,
            ispassenger;
        if (!sscanf(msg, "iii", playerid, vehicleid, ispassenger))
        {
            OnPlayerEnterVehicle(playerid, vehicleid, ispassenger);
        }
    }
}
Download

Links now back on line thanks to some wonderful investigations by Meta!

Filterscript
Include
foreach

This appears to use an older version of foreach, I'll have to update it at some point.

Technical

I just throught I'd add a quick bit of technical information regarding the sync.

Firstly BotSync_SendFast works by utilising "SendClientMessage" and "OnClientMessage", this is the most direct method of generic communication I could find. To identify messages sent via BotSync, rather than any normal method, the color is 0 (i.e. completely see-through black, most people use 0x000000AA for black). If this proves to cause problems I could always change it to 1, as I don't think that's used anywhere at all.

The main chunk of the code relates to the standard communication method (BotSync_Send). This method uses files to write and read messages between the multiple processes - however it's not quite as simple as that as they may try read and write at the same time, so I've added locks.

Basically when either side of the communication wants to write data (note that every bot has two files, one for input, one for output, so, although a file may be locked to one side, both sides can still be writing), they attempt to lock their output file. To lock the output file, first the lock file must not exist, they they must write to the file and read it back again. The reason it is written to and read from is because of what would happen if the server tried to lock a file at the same time as a bot. In this rare case they would both see that the lock file was missing (because there are multiple files, the server would have to be trying to perform a data read while the npc was trying to perform a data write, or vice-versa), and they would BOTH attempt to create it (the chances of this occuring are very slim, but do exist). To avoid this, both sides create the file and write a single character ('0' in NPCS, '1' in the server) and then they both read the first character. Because one of them must have got there first, that one wins and the other side backs off to try again later.

The filterscript is used to buffer, read and write all communications from all server side scripts. Because a lock is required to read and write data to the files, data must be buffered somewhere while the lock is unavailable - this happens in the filterscript (NPCs have their own buffers because they only go one to one). Fast writes go from individual scripts directly, normal writes go to the filterscript, which will write data to various bots as and when it can.

The reason for this complexity is that an NPC can only talk to one server (as there is only one), so that is nice and simple. However a server can talk to many different bots, so it needs code to write to each one when a lock for that single one becomes available.

I know this likely isn't very clear but tough - read the code (it's not commented anyway - oops, forgot).

Credits

I am reposting this include (made by ******) with the thought of what he said to several members of the community. Everything that could help someone should not be deleted from the forums, which is something I agree with since I learned a lot by reading the tutorials on here. He made a lot of things that are used by lots of servers and that knowledge should not be lost for present and future developers.
Reply
#2

Very nice job.
Reply


Forum Jump:


Users browsing this thread: 1 Guest(s)