Creation/Dev/Local Database NPC Communication: Difference between revisions
No edit summary |
(Undo revision 8867 by Special:Contributions/Anti-up (User talk:Anti-up)) |
||
Line 1: | Line 1: | ||
= Introduction = | |||
What exactly is the purpose of database communication? You might have seen the telephone scripts on Era, where anyone can talk to anyone else on another phone on the server. The method that these phones use to transfer messages is database communication. Database communication is really useful for passing data between database instances of NPCs on a server, while at the same time, giving them a friendlier ID. But first, you must understand a couple of things about database NPCs. | |||
''' | Note: This article '''expects''' a certain degree of knowledge of new engine scripting, using classes and database NPCs, etc. | ||
=== What are Database NPCs? === | |||
There are two major types of DB NPC: these are DB NPCs created using [[NC]], and putnpc2()-style NPCs created using scripting, and having your script joined to them using join(). In this tutorial, we will be using both putnpc2()-style NPCs, and DB NPCs. We'll be giving each putnpc2()-style NPC we create a nice friendly identifier. | |||
=== Important concepts === | |||
DB NPCs each have a unique identifier. | |||
[[Image:rc_classlist_normal.png]] NPCs created using putnpc2() each have a unique identifier, which is dynamically generated when the NPC is created. An example of one of these IDs is "localnpc_era_present_32673165_8". Don't worry about remembering these - we will write methods to save these identifiers automatically. | |||
[[Image:rc_localnpcs_normal.png]] NPCs created using NC have a specified identifier that you assign. Again, these must be unique. An example of one of these might be "DB_Phones", or "SkyldTest". | |||
In either a database NPC or a putnpc2() NPC, the ID can be found with the '''this.name''' variable. | |||
= Objective = | |||
The objective for this tutorial is to create a system where NPCs created using putnpc2() can communicate with each other. So, let's start by designing the system. | |||
You will want: | |||
* One Database NPC with a sensible and easy name. For example, if you are creating a system for phones, a suitable name may be "DB_Phones". Try to avoid using "-" in names, since this makes it harder to link to them. | |||
* One script class with your scripting in. In the example of creating phones, you will want to have your phone scripting in here. | |||
* A weapon to place putnpc2() instances of your script class with. | |||
=== Database NPC === | |||
The Database NPC will be one of the core parts of your system. As we've said already, each putnpc2() we have will be assigned a friendly ID, like a phone number. The Database NPC is where these identifiers will be stored, where any NPC can access them. In this case, it's important that our putnpc2() NPCs can read them! | |||
Create a Database NPC. For this example, we'll use "DB_Identifiers". | |||
=== Script classes === | |||
The majority of your scripting will be in these script classes. However, there are a few fundamental things that must be inside this class before you can begin. | |||
==== Saving the ID of the class ==== | |||
When the NPC is placed, it must save it's ID into the database so that we can later access it. In this case, we will generate a random four digit number which will identify the NPC, and then save it to DB_Identifiers. | |||
<pre>function onCreated() | |||
{ | |||
while (this.number == NULL) | |||
{ | |||
temp.try = int(random(1000, 9999); | |||
if (DB_Identifiers.("class_" @ temp.try) == NULL) | |||
{ // Okay, the number is free, let's save it | |||
DB_Identifiers.("class_" @ temp.try) = this.name; | |||
// Now save the number locally so we know it | |||
this.number = temp.try; | |||
break; | |||
} | |||
} | |||
}</pre> | |||
==== Creating a method to find the NPCs by their new ID ==== | |||
So far, the NPC will assign itself a four digit number and save that number into the database. But there are still a few complications. You need a way to find the ID of a class by just it's four digit number. So, we write a function to retrieve a number if it's available. We'll call it findClass(). | |||
<pre>function findClass(temp.number) | |||
{ | |||
// Perform a little sanity check on the number | |||
if (!(temp.number >= 1000 && temp.number <= 9999)) | |||
{ | |||
return -1; // Invalid number, so stop! | |||
} | |||
// Make sure the number is an integer | |||
temp.number = int(temp.number); | |||
// Check if the database has the number saved | |||
if (DB_Identifiers.("class_" @ temp.number) != NULL) | |||
{ // Database has got this identifier, let's return the object ID | |||
return DB_Identifiers.("class_" @ temp.number); | |||
} | |||
else | |||
{ // Database hasn't got the ID, no such NPC? | |||
return -1; | |||
} | |||
}</pre> | |||
Now, from inside your class, you can see if there is a class on the server with your ID just by using findClass(). | |||
To better explain, say you created two NPCs. The first NPC named itself with the number 3209. The second NPC named itself with 5563. If you wanted to find the object for the first NPC, you would use: | |||
<pre>with (findClass(3209)) | |||
{ | |||
// code | |||
}</pre> | |||
... And likewise for the second NPC: | |||
<pre>with (findClass(5563)) | |||
{ | |||
// code | |||
}</pre> | |||
This is exactly how we will make the NPCs communicate. | |||
==== Creating a function to be run remotely ==== | |||
Now, to give the NPC an entry point from other NPCs, we'll create a public function. Public functions can be run by any NPC on the same side (serverside or clientside). | |||
So, let's define a public function called entryPoint(). This is where instances of your class will send data to other instances. This is also where the commands will be interpreted to do something useful. | |||
<pre>public function entryPoint(temp.data) | |||
{ | |||
// Code | |||
}</pre> | |||
Now, the creation of this function means that you can do this: | |||
<pre>findClass(5563).entryPoint("I am sending this data!");</pre> | |||
Now, does this help you? Well, this has given you a foundation to allow your classes to communicate. Hold on, though. There are still some complications. | |||
==== Destroying class instances correctly ==== | |||
If you just this.destroy() an instance of your class without first removing the identifier, the identifier will stay there until someone removes it. So, let's make sure that your routine clears out the identifier properly. | |||
<pre>function destroyInstance() | |||
{ | |||
DB_Identifiers.("class_" @ temp.number) = NULL; | |||
this.destroy(); | |||
}</pre> | |||
Now you can use destroyInstance() to correctly remove your NPC '''and''' it's identifier. | |||
==== An example use of what you've created ==== | |||
Here's an example of something you might want to put in your class to test if it works. | |||
<pre>function onCreated() | |||
{ | |||
while (this.number == NULL) | |||
{ | |||
temp.try = int(random(1000, 9999); | |||
if (DB_Identifiers.("class_" @ temp.try) == NULL) | |||
{ // Okay, the number is free, let's save it | |||
DB_Identifiers.("class_" @ temp.try) = this.name; | |||
// Now save the number locally so we know it | |||
this.number = temp.try; | |||
break; | |||
} | |||
} | |||
} | |||
function findClass(temp.number) | |||
{ | |||
// Perform a little sanity check on the number | |||
if (!(temp.number >= 1000 && temp.number <= 9999)) | |||
{ | |||
return -1; // Invalid number, so stop! | |||
} | |||
// Make sure the number is an integer | |||
temp.number = int(temp.number); | |||
// Check if the database has the number saved | |||
if (DB_Identifiers.("class_" @ temp.number) != NULL) | |||
{ // Database has got this identifier, let's return the object ID | |||
return DB_Identifiers.("class_" @ temp.number); | |||
} | |||
else | |||
{ // Database hasn't got the ID, no such NPC? | |||
return -1; | |||
} | |||
} | |||
public function entryPoint(temp.data) | |||
{ | |||
if (temp.data == "explode") | |||
{ | |||
putexplosion(2, this.x, this.y); | |||
return 1; // Let's let the other NPC know that the command was received | |||
} | |||
} | |||
function onPlayerChats() | |||
{ | |||
temp.tok = player.chat.tokenize(); | |||
if (temp.tok[0] == "explode") | |||
{ | |||
if (findClass(temp.tok[1]).entryPoint("explode") == 1) | |||
{ | |||
player.say2("You successfully exploded the NPC!"); | |||
} | |||
else | |||
{ | |||
player.say2("Maybe the NPC doesn't exist?"); | |||
} | |||
} | |||
}</pre> | |||
You could then say "explode 5563" from any other NPC of your instance, and cause it to explode! | |||
=== Weapon to place your instances === | |||
Let's actually create a method to place your NPCs now. For this, you'll need a weapon. The name of the weapon isn't really important. The code for this is simple. | |||
<pre>function onActionserverside() | |||
{ | |||
if (params[0] == "place") | |||
{ | |||
putnpc2(player.x, player.y, "join yourclassname;"); | |||
} | |||
} | |||
//#CLIENTSIDE | |||
function onPlayerChats() | |||
{ | |||
if (player.chat == "drop") | |||
{ | |||
triggeraction(0, 0, "serverside", "yourweaponname", "place"); | |||
} | |||
}</pre> | |||
= Conclusion = | |||
So, you've got your framework. You could easily now create your system on a framework of these functions by giving each of your NPCs a friendly name. The reason we used four digit numbers here is because they are easy to work with. You might decide to make it 7 digit numbers to resemble phone numbers, or even email addresses! |
Revision as of 02:54, 1 July 2007
Introduction
What exactly is the purpose of database communication? You might have seen the telephone scripts on Era, where anyone can talk to anyone else on another phone on the server. The method that these phones use to transfer messages is database communication. Database communication is really useful for passing data between database instances of NPCs on a server, while at the same time, giving them a friendlier ID. But first, you must understand a couple of things about database NPCs.
Note: This article expects a certain degree of knowledge of new engine scripting, using classes and database NPCs, etc.
What are Database NPCs?
There are two major types of DB NPC: these are DB NPCs created using NC, and putnpc2()-style NPCs created using scripting, and having your script joined to them using join(). In this tutorial, we will be using both putnpc2()-style NPCs, and DB NPCs. We'll be giving each putnpc2()-style NPC we create a nice friendly identifier.
Important concepts
DB NPCs each have a unique identifier.
NPCs created using putnpc2() each have a unique identifier, which is dynamically generated when the NPC is created. An example of one of these IDs is "localnpc_era_present_32673165_8". Don't worry about remembering these - we will write methods to save these identifiers automatically.
NPCs created using NC have a specified identifier that you assign. Again, these must be unique. An example of one of these might be "DB_Phones", or "SkyldTest".
In either a database NPC or a putnpc2() NPC, the ID can be found with the this.name variable.
Objective
The objective for this tutorial is to create a system where NPCs created using putnpc2() can communicate with each other. So, let's start by designing the system.
You will want:
- One Database NPC with a sensible and easy name. For example, if you are creating a system for phones, a suitable name may be "DB_Phones". Try to avoid using "-" in names, since this makes it harder to link to them.
- One script class with your scripting in. In the example of creating phones, you will want to have your phone scripting in here.
- A weapon to place putnpc2() instances of your script class with.
Database NPC
The Database NPC will be one of the core parts of your system. As we've said already, each putnpc2() we have will be assigned a friendly ID, like a phone number. The Database NPC is where these identifiers will be stored, where any NPC can access them. In this case, it's important that our putnpc2() NPCs can read them!
Create a Database NPC. For this example, we'll use "DB_Identifiers".
Script classes
The majority of your scripting will be in these script classes. However, there are a few fundamental things that must be inside this class before you can begin.
Saving the ID of the class
When the NPC is placed, it must save it's ID into the database so that we can later access it. In this case, we will generate a random four digit number which will identify the NPC, and then save it to DB_Identifiers.
function onCreated() { while (this.number == NULL) { temp.try = int(random(1000, 9999); if (DB_Identifiers.("class_" @ temp.try) == NULL) { // Okay, the number is free, let's save it DB_Identifiers.("class_" @ temp.try) = this.name; // Now save the number locally so we know it this.number = temp.try; break; } } }
Creating a method to find the NPCs by their new ID
So far, the NPC will assign itself a four digit number and save that number into the database. But there are still a few complications. You need a way to find the ID of a class by just it's four digit number. So, we write a function to retrieve a number if it's available. We'll call it findClass().
function findClass(temp.number) { // Perform a little sanity check on the number if (!(temp.number >= 1000 && temp.number <= 9999)) { return -1; // Invalid number, so stop! } // Make sure the number is an integer temp.number = int(temp.number); // Check if the database has the number saved if (DB_Identifiers.("class_" @ temp.number) != NULL) { // Database has got this identifier, let's return the object ID return DB_Identifiers.("class_" @ temp.number); } else { // Database hasn't got the ID, no such NPC? return -1; } }
Now, from inside your class, you can see if there is a class on the server with your ID just by using findClass().
To better explain, say you created two NPCs. The first NPC named itself with the number 3209. The second NPC named itself with 5563. If you wanted to find the object for the first NPC, you would use:
with (findClass(3209)) { // code }
... And likewise for the second NPC:
with (findClass(5563)) { // code }
This is exactly how we will make the NPCs communicate.
Creating a function to be run remotely
Now, to give the NPC an entry point from other NPCs, we'll create a public function. Public functions can be run by any NPC on the same side (serverside or clientside).
So, let's define a public function called entryPoint(). This is where instances of your class will send data to other instances. This is also where the commands will be interpreted to do something useful.
public function entryPoint(temp.data) { // Code }
Now, the creation of this function means that you can do this:
findClass(5563).entryPoint("I am sending this data!");
Now, does this help you? Well, this has given you a foundation to allow your classes to communicate. Hold on, though. There are still some complications.
Destroying class instances correctly
If you just this.destroy() an instance of your class without first removing the identifier, the identifier will stay there until someone removes it. So, let's make sure that your routine clears out the identifier properly.
function destroyInstance() { DB_Identifiers.("class_" @ temp.number) = NULL; this.destroy(); }
Now you can use destroyInstance() to correctly remove your NPC and it's identifier.
An example use of what you've created
Here's an example of something you might want to put in your class to test if it works.
function onCreated() { while (this.number == NULL) { temp.try = int(random(1000, 9999); if (DB_Identifiers.("class_" @ temp.try) == NULL) { // Okay, the number is free, let's save it DB_Identifiers.("class_" @ temp.try) = this.name; // Now save the number locally so we know it this.number = temp.try; break; } } } function findClass(temp.number) { // Perform a little sanity check on the number if (!(temp.number >= 1000 && temp.number <= 9999)) { return -1; // Invalid number, so stop! } // Make sure the number is an integer temp.number = int(temp.number); // Check if the database has the number saved if (DB_Identifiers.("class_" @ temp.number) != NULL) { // Database has got this identifier, let's return the object ID return DB_Identifiers.("class_" @ temp.number); } else { // Database hasn't got the ID, no such NPC? return -1; } } public function entryPoint(temp.data) { if (temp.data == "explode") { putexplosion(2, this.x, this.y); return 1; // Let's let the other NPC know that the command was received } } function onPlayerChats() { temp.tok = player.chat.tokenize(); if (temp.tok[0] == "explode") { if (findClass(temp.tok[1]).entryPoint("explode") == 1) { player.say2("You successfully exploded the NPC!"); } else { player.say2("Maybe the NPC doesn't exist?"); } } }
You could then say "explode 5563" from any other NPC of your instance, and cause it to explode!
Weapon to place your instances
Let's actually create a method to place your NPCs now. For this, you'll need a weapon. The name of the weapon isn't really important. The code for this is simple.
function onActionserverside() { if (params[0] == "place") { putnpc2(player.x, player.y, "join yourclassname;"); } } //#CLIENTSIDE function onPlayerChats() { if (player.chat == "drop") { triggeraction(0, 0, "serverside", "yourweaponname", "place"); } }
Conclusion
So, you've got your framework. You could easily now create your system on a framework of these functions by giving each of your NPCs a friendly name. The reason we used four digit numbers here is because they are easy to work with. You might decide to make it 7 digit numbers to resemble phone numbers, or even email addresses!