The Gang Garrison 2 Forum

Gang Garrison Development => Game Modifications => Add-Ons and Plugins => Topic started by: ajf on March 12, 2013, 04:17:46 pm

Title: Server-sent plugins FAQ
Post by: ajf on March 12, 2013, 04:17:46 pm
What is a "server-sent plugin"?
Server-sent plugins are a new feature in GG2 version 2.5. They allow servers to have a set of plugins which the server and players automatically download.

Despite the name, "server-sent" plugins are not sent by the server. Rather, the server sends a list of plugin names, which are used to get the plugins from http://www.ganggarrison.com/plugins/ (http://www.ganggarrison.com/plugins/) (see "How safe are they?" below)

Why don't they work in Gang Garrison 2.5.x any more?
See this topic: http://www.ganggarrison.com/forums/index.php?topic=34113.0 (http://www.ganggarrison.com/forums/index.php?topic=34113.0)

Why do I get an error when I extract it to my plugins folder and run it?
Server-sent plugins are not supposed to be downloaded by you. Instead, just put the names of the plugins you want to use in your gg2.ini file (see "How do I use server-sent plugins?") and they will be automatically downloaded by your server and its players.

Plugins can detect whether they are running as server-sent plugins by checking whether "isServerSentPlugin" is set to true on their PluginEnvironment instance.

How do I use server-sent plugins?
If you want to host a server using server-sent plugins, find out the names of the plugins you want to use. You don't need to download them. Just edit the following two lines in your gg2.ini file:
Code: [Select]
ServerPluginList=
ServerPluginsRequired=0
After ServerPluginList you can put a comma-separated list of plugin names (see: http://www.ganggarrison.com/plugins/ (http://www.ganggarrison.com/plugins/)). There must not be any spaces between the plugin names and the commas. Here's an example:
Code: [Select]
ServerPluginList=lowgravity,can_you_feel_the_sunshineAfter ServerPluginsRequired you can either put 1 (requires plugins to join server) or 0 (plugins are optional but suggested to client).
If you don't want any plugins, just leave the ServerPluginList= field blank.

If you want to join a server using server-sent plugins, you'll be prompted if you want to download them. If they are required, you can either download them or disconnect. If they aren't required, you can choose to continue connecting without downloading them. You can disable the prompts in the settings. If you do this, it will download them without asking you. However, this might be dangerous. (see "How safe are they?" below)

How safe are they?
Plugins can do almost anything. They can download applications and execute them, delete files, spy on you, etc. Because of this, server-sent plugins are not downloaded like maps are, because then a server could send a malicious plugin. Instead, server-sent plugins are always downloaded from http://www.ganggarrison.com/plugins/ (http://www.ganggarrison.com/plugins/), which only has plugins that have been screened by me or other developers. Because of this you shouldn't need to worry, but we are not perfect and something might slip past us. If you are paranoid, avoid joining servers with plugins.

How do I make a server-sent plugin?
A server-sent plugin is just a zip file with a plugin.gml file in it. When it is downloaded, it is extracted to a temporary folder, and plugin.gml is executed with argument0 being the path of the temporary folder (does NOT end in a backslash, add it yourself), so that the plugin can find any resource files that were in the zip file. The temporary folder is cleaned up for you when GG2 exits.

Plugin names must be composed only of lowercase letters, digits and underscores, and be a minimum of one letter long. If the plugin name doesn't fit this format, GG2 will error and won't load it.

When the user leaves that server, they have to quit or restart GG2, so don't worry about having to disable/enable your plugin. Server-sent plugins for a given host server are also executed on the host server itself, so you should account for this with global.isHost checks.

When you want to debug a server-sent plugin, you can create a special folder called ServerPluginsDebug in your GG2 folder, and place your plugin zip file in there with the correct .zip extension, e.g. lowgravity.zip. GG2 will check in that folder first before trying to download it.

Once you have made your server-sent plugin, you can get it reviewed and hopefully onto http://www.ganggarrison.com/plugins/ (http://www.ganggarrison.com/plugins/) so other people can use it. Send me a PM with a link to the forum thread for the plugin, its name and the zip file, and I'll review it and either add it or ask you to change some things before it can be added. Please agree to the terms and conditions (http://www.ganggarrison.com/forums/index.php?topic=36590.0).

If you're looking for server-sent plugin examples, just download one of the existing plugins: http://www.ganggarrison.com/plugins/ (http://www.ganggarrison.com/plugins/)

How do I do networking with server-sent plugins?

From Gang Garrison 2.7.3 onwards, server-sent plugins are executed within a PluginEnvironment instance with four properties: directory (directory the plugin was extracted to), packetID (the packetID value), isServerSentPlugin (set to 1) and isLocalPlugin (set to 0). Prior to 2.7.3, the only way to get the packetID and directory was from argument1 and argument0 respectively, which are still set for compatibility.

All server-sent plugins now get a special packetID that can be used with the PluginPacket* functions to send buffers encapsulated in the new PLUGIN_PACKET packet, and to receive them. Receiving, clients get just the buffer (from the server), servers get the buffer (from the client) and the Player object (corresponding to the client). Sending, servers send the buffer to all clients, or a specific client, and clients send the buffer to just the server.

The buffers mentioned here are Faucet Networking buffers, since Gang Garrison 2 uses MedO's Faucet Networking extension. Full documentation on Faucet Networking is provided in the form of a PDF included in the ZIP file for each release. You can find Faucet Networking releases here on GitHub: https://github.com/Medo42/Faucet-Networking-Extension/releases (https://github.com/Medo42/Faucet-Networking-Extension/releases) (at the time of writing, GG2 used version 1.2.1). You need not concern yourself with any of the TCP or UDP functions, only how buffers work.


How does plugin caching work?

It "just works" and you don't have to worry about it. The first time your client sees a particular version of a plugin, it will save a copy in the cache. Later, if it needs that particular version of that plugin again, it will retrieve it from the cache, instead of downloading it again. If you delete the cache (the ServerPluginsCache folder), nothing bad will happen, but GG2 will have to redownload any plugins that were in that cache when it next needs them.
Title: Re: Server-sent plugins FAQ
Post by: Dusty on May 26, 2013, 02:51:51 am
Could we get an example of how to use pluginetworking?
Title: Re: Server-sent plugins FAQ
Post by: Lorgan on May 26, 2013, 03:09:56 am
You could look at the already made server-sent plugins :P
Removed the actual functional part of my space plugin and replaced it with a comment (you still have to bear with the space themed variables though)

Code: (top of the plugin) [Select]
global.spacePacketID = argument1;
Code: (step event of any object, i usuallly use PlayerControl) [Select]
   //send your custom event
   if keyboard_check_pressed(global.down) && global.myself.object != -1 && !instance_exists(InGameMenuController) {
        var spaceBuffer;
        spaceBuffer = buffer_create();
        write_ubyte(spaceBuffer,ds_list_find_index(global.players,global.myself));
        PluginPacketSend(global.spacePacketID,spaceBuffer);
        buffer_destroy(spaceBuffer);
        if global.isHost {
            //The host doesn't send the event to itself so that should be handled here
        }
    }
 
    //process the custom event
    if global.isHost {
        while(PluginPacketGetBuffer(global.spacePacketID) != -1) {
            var spaceBuffer, space_player;
            spaceBuffer = PluginPacketGetBuffer(global.spacePacketID);
            space_player = PluginPacketGetPlayer(global.spacePacketID);
            if space_player.object != -1 && instance_exists(space_player.object) {
                //The host executes the event for other players here (if the player has an object in this case)
               
                buffer_clear(spaceBuffer);
                write_ubyte(spaceBuffer, ds_list_find_index(global.players,space_player));
                PluginPacketSend(global.spacePacketID,spaceBuffer);
            }
           
            buffer_destroy(spaceBuffer);
            PluginPacketPop(global.spacePacketID);
        }
    } else {
        while(PluginPacketGetBuffer(global.spacePacketID) != -1) {
            var spaceBuffer, space_player;
            spaceBuffer = PluginPacketGetBuffer(global.spacePacketID);
            space_player = ds_list_find_value(global.players,read_ubyte(spaceBuffer));
            if space_player != -1 {
                if space_player.object != -1 && instance_exists(space_player.object) {
                    //The client executes the event for any player here.
                }
            }       
            buffer_destroy(spaceBuffer);
            PluginPacketPop(global.spacePacketID);
        }
    }
Title: Re: Server-sent plugins FAQ
Post by: ajf on May 26, 2013, 03:49:08 am
Btw, no need to destroy buffers that you got from PluginPacketGetBuffer, since PluginPacketPop does that for you. Also, it's not a good idea to use the same variable name for sending and receiving buffers.
Title: Re: Server-sent plugins FAQ
Post by: Lorgan on May 26, 2013, 03:54:48 am
ok :P
Title: Re: Server-sent plugins FAQ
Post by: Dusty on May 26, 2013, 04:05:28 am
So, I made a folder called "ServerPluginsDebug", dropped a .zip file, named Sprites (lazy naming), with a plugin.gml into it, and added Sprites to my .ini
Apparently it's an invalid plugin.
Oh. It seems like it HAS to be lowercase. That seems silly.
Title: Re: Server-sent plugins FAQ
Post by: ajf on May 26, 2013, 05:00:49 am
So, I made a folder called "ServerPluginsDebug", dropped a .zip file, named Sprites (lazy naming), with a plugin.gml into it, and added Sprites to my .ini
Apparently it's an invalid plugin.
Oh. It seems like it HAS to be lowercase. That seems silly.
I didn't want to type out the 26 uppercase characters too :P
Title: Re: Server-sent plugins FAQ
Post by: ajf on July 07, 2013, 08:33:14 pm
Updated to note caching.
Title: Re: Server-sent plugins FAQ
Post by: notarctic on August 01, 2013, 12:49:18 am
Quote
Why don't they work in Gang Garrison 2.5.x any more?
See this topic: http://www.ganggarrison.com/forums/index.php?topic=34113.0 (http://www.ganggarrison.com/forums/index.php?topic=34113.0)
for anyone using 2.5 as a mod (highly doubt it but meh) here's the legacy repository you can direct your game to
http://arcticmustang.github.io/gg2plugins (http://arctic5.github.io/gg2plugins)
Title: Re: Server-sent plugins FAQ
Post by: ajf on August 31, 2013, 04:08:29 pm
Quote
Why don't they work in Gang Garrison 2.5.x any more?
See this topic: http://www.ganggarrison.com/forums/index.php?topic=34113.0 (http://www.ganggarrison.com/forums/index.php?topic=34113.0)
for anyone using 2.5 as a mod (highly doubt it but meh) here's the legacy repository you can direct your game to
http://arcticmustang.github.io/gg2plugins (http://arcticmustang.github.io/gg2plugins)
orrrrr just use ServerPluginsDebug?
Title: Re: Server-sent plugins FAQ
Post by: iLegend on August 31, 2013, 07:48:01 pm
sooooo how exactly do you test server sent plugins.
Title: Re: Server-sent plugins FAQ
Post by: notarctic on August 31, 2013, 08:02:07 pm
sooooo how exactly do you test server sent plugins.
uh like what are you trying to do?
Title: Re: Server-sent plugins FAQ
Post by: ajf on August 31, 2013, 08:22:59 pm
sooooo how exactly do you test server sent plugins.
/ServerPluginsDebug?
Title: Re: Server-sent plugins FAQ
Post by: notarctic on August 31, 2013, 08:24:29 pm
sooooo how exactly do you test server sent plugins.
/ServerPluginsDebug?
stick your plugin in there and put the plugin name into the ini
Title: Re: Server-sent plugins FAQ
Post by: iLegend on August 31, 2013, 10:02:47 pm
its pretty annoying zipping the plugin every time I wanna test it :(
Title: Re: Server-sent plugins FAQ
Post by: notarctic on August 31, 2013, 10:03:42 pm
its pretty annoying zipping the plugin every time I wanna test it :(
ikr?
Title: Re: Server-sent plugins FAQ
Post by: ajf on August 31, 2013, 10:07:26 pm
its pretty annoying zipping the plugin every time I wanna test it :(
use 7zip
Title: Re: Server-sent plugins FAQ
Post by: iLegend on August 31, 2013, 10:12:58 pm
it's okay i found out winrar updates the temp file if it doesn't match with the archive one.

anyway is there any way to log a variable or something. for debugging purposes.
Title: Re: Server-sent plugins FAQ
Post by: Danikah on January 29, 2014, 03:20:09 pm
Question about networking in general:

So, these buffers are just numbers sent to clients or the server. And to actually get a working stuff out of it, I should check the buffer we just received, do the actions related to that buffer, and destroy the buffer, right?
But then how do we check for more data? Like, if a player dies, his client sends his death to the server with the buffer '1'. The server gets it, but how can it send the data forward to all clients if all he got that a character died out of the 32, maybe?
Title: Re: Server-sent plugins FAQ
Post by: ajf on January 29, 2014, 07:15:01 pm
Question about networking in general:

So, these buffers are just numbers sent to clients or the server. And to actually get a working stuff out of it, I should check the buffer we just received, do the actions related to that buffer, and destroy the buffer, right?
But then how do we check for more data? Like, if a player dies, his client sends his death to the server with the buffer '1'. The server gets it, but how can it send the data forward to all clients if all he got that a character died out of the 32, maybe?
PluginPacketGetPlayer tells you from which client you got that buffer
Title: Re: Server-sent plugins FAQ
Post by: Danikah on January 30, 2014, 07:58:24 am
Question about networking in general:

So, these buffers are just numbers sent to clients or the server. And to actually get a working stuff out of it, I should check the buffer we just received, do the actions related to that buffer, and destroy the buffer, right?
But then how do we check for more data? Like, if a player dies, his client sends his death to the server with the buffer '1'. The server gets it, but how can it send the data forward to all clients if all he got that a character died out of the 32, maybe?
PluginPacketGetPlayer tells you from which client you got that buffer
Well, that neither helped nor answered my question.

EDIT: I think I got it.

I could do:
Code: [Select]
PluginPacketSend(packetID, 1);
PluginPacketSend(packetID, PluginPacketGetPlayer);
PluginPacketSend(packetID, 0);
For example as a server to send what data we received from the client when his player died to other clients. Then I just have to read these in order to interpret what happened as a client, like this:
Code: [Select]
if PluginPacketGetBuffer(packetID) == 1 //if a player death happened
{
    playerid = PluginPacketGetBuffer(packetID);
    gib = PluginPacketGetBuffer(packetID);
}
No?
I have a feeling it's no. Never understood networking, every 39dll networking tutorial (or just networking tutorials in general) I have ever read so far have seemed ungraspable or under-explained to me.
Title: Re: Server-sent plugins FAQ
Post by: ajf on January 30, 2014, 03:13:14 pm
Question about networking in general:

So, these buffers are just numbers sent to clients or the server. And to actually get a working stuff out of it, I should check the buffer we just received, do the actions related to that buffer, and destroy the buffer, right?
But then how do we check for more data? Like, if a player dies, his client sends his death to the server with the buffer '1'. The server gets it, but how can it send the data forward to all clients if all he got that a character died out of the 32, maybe?
PluginPacketGetPlayer tells you from which client you got that buffer
Well, that neither helped nor answered my question.

EDIT: I think I got it.

I could do:
Code: [Select]
PluginPacketSend(packetID, 1);
PluginPacketSend(packetID, PluginPacketGetPlayer);
PluginPacketSend(packetID, 0);
For example as a server to send what data we received from the client when his player died to other clients. Then I just have to read these in order to interpret what happened as a client, like this:
Code: [Select]
if PluginPacketGetBuffer(packetID) == 1 //if a player death happened
{
    playerid = PluginPacketGetBuffer(packetID);
    gib = PluginPacketGetBuffer(packetID);
}
No?
I have a feeling it's no. Never understood networking, every 39dll networking tutorial (or just networking tutorials in general) I have ever read so far have seemed ungraspable or under-explained to me.
...No, you really didn't get it.

A faucet net buffer is a set of bytes. If I do this:

Code: [Select]
var buf;
buf = buffer_create();

I get a fresh buffer, and buf will contain the buffer's ID. The buffer is empty. To add a byte to that buffer, I'll use write_ubyte:

Code: [Select]
var buf;
buf = buffer_create();
write_ubyte(buf, 74);

The "u" in "ubyte" means an unsigned byte, that is, no positive or negative sign. An unsigned byte can be any number from 0 to 255. A signed byte would be a number from -128 to 127. So, now our buffer contains that byte we wrote to it. We're on the server, so let's send that in a packet to everyone on the server:

Code: [Select]
var buf;
buf = buffer_create();
write_ubyte(buf, 74);
PluginPacketSend(packetID, buf);

packetID is the value of argument1 when plugin.gml is executed, it's the ID given by GG2 to a plugin so that GG2 knows which plugin it is when it tries to send packets. That buffer can now be destroyed if we want, since its value has been copied by GG2 and sent to all of our clients:

Code: [Select]
var buf;
buf = buffer_create();
write_ubyte(buf, 74);
PluginPacketSend(packetID, buf);
buffer_destroy(buf);

Now on the client we want to see if we've got a new message from the server yet. So we'll check if PluginPacketGetBuffer returns -1 or not:

Code: [Select]
if (PluginPacketGetBuffer(packetID) != -1)
Then we can try getting the buffer itself:

Code: [Select]
if (PluginPacketGetBuffer(packetID) != -1) {
    var recvBuf;
    recvBuf = PluginPacketGetBuffer(packetID);
}

We're able to call PluginPacketGetBuffer twice because it only gets you the first buffer in the queue, it doesn't remove it. Now that we have our buffer, let's read a byte from it:

Code: [Select]
if (PluginPacketGetBuffer(packetID) != -1) {
    var recvBuf;
    recvBuf = PluginPacketGetBuffer(packetID);
    var someValue;
    someValue = read_ubyte(recvBuf);
}

someValue should now contain the value of the first byte in the buffer. If this was the packet we sent earlier from the server, it should be 74. Let's say that 74 has some special significance and that we want to respond to it. We could do that like this:

Code: [Select]
if (PluginPacketGetBuffer(packetID) != -1) {
    var recvBuf;
    recvBuf = PluginPacketGetBuffer(packetID);
    var someValue;
    someValue = read_ubyte(recvBuf);
    if (someValue == 74) {
        var sendBuf;
        sendBuf = buffer_create();
        write_ubyte(sendBuf, 31);
        PluginPacketSend(packetID, sendBuf);
        buffer_destroy(sendBuf);
    }
}

This means that if the client receives a packet with its first byte being 74, it'll send a packet back to the server with the first byte being 31. However, at present, this code will just look at the first packet it received and not get rid of it. This means that if we run that code later, it'll just look at the same packet again. So, we need to discard that packet from the queue:

Code: [Select]
if (PluginPacketGetBuffer(packetID) != -1) {
    var recvBuf;
    recvBuf = PluginPacketGetBuffer(packetID);
    var someValue;
    someValue = read_ubyte(recvBuf);
    if (someValue == 74) {
        var sendBuf;
        sendBuf = buffer_create();
        write_ubyte(sendBuf, 31);
        PluginPacketSend(packetID, sendBuf);
        buffer_destroy(sendBuf);
    }
    PluginPacketPop(packetID);
}

Note how I didn't use buffer_destroy here. We don't need to, because PluginPacketPop does that for us. As an additional improvement, if we're running this code every step, it'd only handle one packet at a time, which probably isn't ideal. Let's use a while loop, so we'll look at each packet one by one until there are none left:

Code: [Select]
while (PluginPacketGetBuffer(packetID) != -1) {
    var recvBuf;
    recvBuf = PluginPacketGetBuffer(packetID);
    var someValue;
    someValue = read_ubyte(recvBuf);
    if (someValue == 74) {
        var sendBuf;
        sendBuf = buffer_create();
        write_ubyte(sendBuf, 31);
        PluginPacketSend(packetID, sendBuf);
        buffer_destroy(sendBuf);
    }
    PluginPacketPop(packetID);
}

So, our clients can now handle received packets, and if a packet begins with an unsigned byte of value 74, it will send a packet back to the server with the value 31 as the first byte. Now we just need our server to be able to respond to packets too, so here's code for the server to do that:

Code: [Select]
while (PluginPacketGetBuffer(packetID) != -1) {
    var recvBuf;
    recvBuf = PluginPacketGetBuffer(packetID);
    var someValue;
    someValue = read_ubyte(recvBuf);
    if (someValue == 31) {
        // We got a 31 packet!
    }
    PluginPacketPop(packetID);
}

But wait. What if we want to know which client sent us this packet? How do we know who? Well, for that you'll use PluginPacketGetPlayer, which complements PluginPacketGetBuffer and gets you the Player object for the client that sent us this packet:

Code: [Select]
while (PluginPacketGetBuffer(packetID) != -1) {
    var recvBuf;
    recvBuf = PluginPacketGetBuffer(packetID);
    var someValue;
    someValue = read_ubyte(recvBuf);
    if (someValue == 31) {
        // We got a 31 packet!
        var player;
        player = PluginPacketGetPlayer(packetId);
        show_message(player.name + " sent us a 34 packet!");
    }
    PluginPacketPop(packetID);
}

...and that should answer your questions, hopefully. I should note that PluginPacketGetPlayer doesn't work at all on the client because the client will only ever receive packets from the server. Its use is for the server to know which client sent it a packet.
Title: Re: Server-sent plugins FAQ
Post by: Danikah on January 30, 2014, 04:10:08 pm
Ooooooh!

Now I understand! Thank you, ajf.
I might not use it in the near future, but at least I have some understanding of it now, or rather, the logic of it.
I could deduce most of the things I want from it. Thank you again!
Title: Re: Server-sent plugins FAQ
Post by: ajf on January 30, 2014, 06:10:07 pm
Ooooooh!

Now I understand! Thank you, ajf.
I might not use it in the near future, but at least I have some understanding of it now, or rather, the logic of it.
I could deduce most of the things I want from it. Thank you again!
Glad to help! I hope this might help others, too, considering how minimal the documentation in the OP is, which assumes an understanding of Faucet Net buffers already.
Title: Re: Server-sent plugins FAQ
Post by: ZaSpai on February 26, 2014, 08:10:33 pm
Question: if a server host uses an unapproved/unauthorized SSP run from ServerPluginsDebug, is a client (excluding a multiclient from the same machine) able to download that unauthorized SSP from the host, or will the attempt to join fail?
Title: Re: Server-sent plugins FAQ
Post by: notarctic on February 26, 2014, 10:40:18 pm
its simple, it wont work. iirc the message you get from loading from using debug describes everything. even if the client manages to join, depending on what you're doing, the client will receive unexpected data or desync or both
Title: Re: Server-sent plugins FAQ
Post by: ZaSpai on February 26, 2014, 10:43:33 pm
its simple, it wont work. iirc the message you get from loading from using debug describes everything. even if the client manages to join, depending on what you're doing, the client will receive unexpected data or desync or both
So essentially the client will need to have the SSP in question in their ServerPluginsDebug as well I probably meant ServerPluginsCache or something nvm it was ServerPluginsDebug for anything to work

Coolies
Title: Re: Server-sent plugins FAQ
Post by: ajf on February 27, 2014, 07:15:46 am
its simple, it wont work. iirc the message you get from loading from using debug describes everything. even if the client manages to join, depending on what you're doing, the client will receive unexpected data or desync or both
So essentially the client will need to have the SSP in question in their ServerPluginsDebug as well I probably meant ServerPluginsCache or something nvm it was ServerPluginsDebug for anything to work

Coolies
Right.

When the server starts, it finds the MD5 hash of each plugin and generates a new list containing that info, e.g. can_you_feel_the_sunshine@ca77b909b9fdf6655103dcdada4032b0,buffpyro@96fb7f3f779948f40935b89fc3b56399.

The client is sent that list when it connects. It'll then look in three sources, in order:

If your plugin in ServerPluginsDebug happened to have the same name as an actual plugin, and happened to have the same MD5 as an actual version if that plugin (which is unlikely unless it actually is that version of that plugin), then your client would be able to download it from the server fine. Most likely, however, that won't be the case, and it'll bring up an error saying it couldn't find the plugin on the server, unless you had it in ServerPluginsDebug or ServerPluginsCache.
Title: Re: Server-sent plugins FAQ
Post by: ajf on July 13, 2014, 06:31:31 am
Updated to note v2.7.3's PluginEnvironments.

A summary of the changes can be found here:

btw what changes with the 'plugins run in own environment'?
Ah, I should update the plugin FAQ about that. Basically, we spawn a new PluginEnvironment instance (it's a persistent, invisible object) and do with(env) { execute_file(...); } so any variables you forget to define will end up on that object instead of on GameServer, Client or the weird non-object game_init runs in.

It also means you can now just type 'packetID' and 'directory' to get the packet ID or directory, respectively, since we put those as variables on the object for you. There's also isLocalPlugin and isServerSentPlugin for checking what mode the plugin's running in.

This also gives you a nice place to stick your variables. For my latest plugin, I did this:

globalvar myplugin;
myplugin = id; // this PluginEnvironment object

blah = 3;
foobar = "stuff";

...

if (myplugin.blah) {
    ...
}

Title: Re: Server-sent plugins FAQ
Post by: ajf on July 29, 2014, 08:40:21 pm
The OP now mentions where to find Faucet Networking documentation, by arctic's request. Which is a good idea, since it's not immediately obvious that the docs are in PDF form in the release ZIPs.