Log in

View Full Version : Execute GM Command in Perl


revloc02c
10-31-2011, 08:37 PM
Is there a way to execute a GM Command from the Perl script?

(I asked almost the same question here (http://www.eqemulator.org/forums/showthread.php?p=204411#post204411), but I needed to bump that post anyway.)

Caryatis
10-31-2011, 08:44 PM
You should explain why you want to do it.

In regards to task reloadall from perl, I can't think of any reason why you would want that. If you have a valid reason then Im sure somebody will help you but if its not then you will most likely be pointed in the direction you should be going.

revloc02c
11-01-2011, 06:02 PM
I've got a SQL stored procedure that takes the PCs level, and then randomly chooses a NPC_ID from the spawn_entry table near the PCs level so a quest can be assigned to kill this random creature. Well, what I need is a way of determining when the PC actually kills this randomly assigned MOB so that when they return I can reward them. (I can put the npc_type id in a qglobal, but I still have no way of knowing when or whether they killed the monster.) So I know the task system can identify when a MOB dies, so as a part of getting the quest I used SQL to dynamically create a new task (with all the appropriate activities etc.) that can be assigned to the PC (it is actually another stored procedure but that is irrelevant). Problem is that the running server does not recognize the newly created task...unless/until I use the gm command #task reloadall; then the Perl code can just do a #quest::assigntask(@taskID[0]); and it works just fine.

So I need to either:
A) Somehow track a kill--sorta like the task system does--when the PC kills a randomly assigned MOB somehow flag that
OR
B) Once the task is dynamically created with the sproc, do a GM "#task reloadall" in the Perl script so that I can just quest::assigntask the task

Caryatis
11-01-2011, 08:08 PM
The biggest flaw in your idea(imo) is that you are going to be creating hundreds of tasks automatically, even more if you do this on a popular server and the task is repeatable. If you had this capability, you would probably use it again, then you have thousands of tasks that are only ever used once, unless the code also checks each task to see if the target matches the random mob.

I doubt that would affect anything besides the load time on taskmaster however its pretty sloppy design and could lead to performance issues.

If I was going to do this I would set a global for the NPCType and then add a sub EVENT_DEATH check for the global in the default.pl in templates, if the global matches the player and zone then updatetaskactivity. This is not an ideal solution as it won't work for mobs with scripts that have a death event and its still pretty sloppy imo. It would also require a custom perl command as you would need to set the zone field to the zone you wish the mob killed in as opposed to the zone that the npc setting the global is in(and I figure if you are in there adding that, you would probably just be adding the reloadall).

So I guess in the end I was no help, pretty usual.

revloc02c
11-03-2011, 12:09 AM
The biggest flaw in your idea(imo) is that you are going to be creating hundreds of tasks automatically, even more if you do this on a popular server and the task is repeatable. If you had this capability, you would probably use it again, then you have thousands of tasks that are only ever used once,
Yeah, if I had gotten this to actual work I was going to delete the task from the database once the player completed it. Using the task system was only a means to an end--to track when the kill was made.

unless the code also checks each task to see if the target matches the random mob.
This actually is a really good idea except here (http://www.eqemulator.net/wiki/wikka.php?wakka=TaskSystemDBTables) I read that "the maximum task id is 9999," thus what I said about clearing them out.

If I was going to do this I would set a global for the NPCType and then add a sub EVENT_DEATH check for the global in the default.pl in templates, if the global matches the player and zone then updatetaskactivity. This is not an ideal solution as it won't work for mobs with scripts that have a death event and its still pretty sloppy imo. It would also require a custom perl command as you would need to set the zone field to the zone you wish the mob killed in as opposed to the zone that the npc setting the global is in(and I figure if you are in there adding that, you would probably just be adding the reloadall).
This is really some good thinking. I appreciate you taking the time to talk it out. I need to think through some similar scenarios and I believe that eventually I'll figure out something that will work.

So I guess in the end I was no help, pretty usual.
Actually you provided a lot of food for thought which is half the fun for me, thanks.

An alternative route might be to randomly select an item dropped by the random MOB and then use that as a turn in. Easy to store. Easy to track. There are loopholes, the PC could just get the item from somewhere else and never kill the assigned MOB, but maybe I'll just call that a feature rather than a bug.

revloc02c
11-04-2011, 12:16 AM
So back to the original issue, executing a GM command from Perl script. Can anyone offer some direction or advice on how easy or hard this would be to write some custom code that does it or allows it? Are they closely related, or miles apart?

lerxst2112
11-04-2011, 12:27 AM
You would need to add a new function exposed to perl. It's not hard to do, and there are many examples available. Once you have that you need to execute the same code the command does.

If you're lucky the command just calls another function to do the work and your perl function can do the same.

If you're unlucky the command has the code inside it. In that case you would need to move that code to another function that both can call.

trevius
11-04-2011, 10:48 AM
There has been work done to allow using gm commands from perl, but it doesn't work yet. I think KLS said she wanted to implement it at some point, but I don't think it is too high on the priority list.

I think Caryatis was the closest to the solution you need. You should be able to use default.pl in your templates folder to give the same script to all NPCs on your server that don't have a script assigned to them already in the zone they are in.

You can write a simple EVENT_KILLED_MERIT in your default.pl that sets a qglobal on the character, which can be checked by your quest NPC later. That will be your way of verifying they killed the NPC.

As Caryatis mentioned, this would cause issues with any NPCs that have a script assigned to them already, which wouldn't work with the templates/default.pl and would break your system. My only solution to that would be to also check for existing script files for the NPC you randomly selected. So, you would get the NPC name from the DB and do a file search within your quests folder with perl to find a .pl file for that NPC name. I don't know how to search for the file off the top of my head, but I assume it is most likely possible with a little googling. You would also have to account for any names that have a ` in them, as they would need to be replaced with - when searching for the file name. If you also query spawn points and find the zone that the NPC spawns in, you can use that to find the zone short name so you know the zone folder to search in for quests. You will need to do that anyway if any of your zones have an existing default.pl within them as that will override the templates/default.pl and break your system. If your script finds any perl scripts for the NPC it selected, it just keeps looping through another randomly selected NPCID until it gets one that doesn't have a script.

The idea is kinda neat, but I have a feeling it is going to be a bit more work than you may expect for it to be flawless. You will have to account for zones that may not be fully implemented or maybe not even have any way for players to access. You will also probably have to check spawn points to make sure the NPC isn't a quest spawned only NPC. I am sure there are other things you will run into in the process of trying to implement it.

revloc02c
11-05-2011, 06:28 PM
You would need to add a new function exposed to perl. It's not hard to do, and there are many examples available. Once you have that you need to execute the same code the command does.

If you're lucky the command just calls another function to do the work and your perl function can do the same.

If you're unlucky the command has the code inside it. In that case you would need to move that code to another function that both can call.
Thanks lerxst this is just enough to get me started on the right foot. I post code if I figure something out, but don't hold your breath...even if I do it'll take a while, RL takes up a lot of my time.

There has been work done to allow using gm commands from perl, but it doesn't work yet. I think KLS said she wanted to implement it at some point, but I don't think it is too high on the priority list.

I think Caryatis was the closest to the solution you need. You should be able to use default.pl in your templates folder to give the same script to all NPCs on your server that don't have a script assigned to them already in the zone they are in.

You can write a simple EVENT_KILLED_MERIT in your default.pl that sets a qglobal on the character, which can be checked by your quest NPC later. That will be your way of verifying they killed the NPC.

As Caryatis mentioned, this would cause issues with any NPCs that have a script assigned to them already, which wouldn't work with the templates/default.pl and would break your system. My only solution to that would be to also check for existing script files for the NPC you randomly selected. So, you would get the NPC name from the DB and do a file search within your quests folder with perl to find a .pl file for that NPC name. I don't know how to search for the file off the top of my head, but I assume it is most likely possible with a little googling. You would also have to account for any names that have a ` in them, as they would need to be replaced with - when searching for the file name. If you also query spawn points and find the zone that the NPC spawns in, you can use that to find the zone short name so you know the zone folder to search in for quests. You will need to do that anyway if any of your zones have an existing default.pl within them as that will override the templates/default.pl and break your system. If your script finds any perl scripts for the NPC it selected, it just keeps looping through another randomly selected NPCID until it gets one that doesn't have a script.

The idea is kinda neat, but I have a feeling it is going to be a bit more work than you may expect for it to be flawless. You will have to account for zones that may not be fully implemented or maybe not even have any way for players to access. You will also probably have to check spawn points to make sure the NPC isn't a quest spawned only NPC. I am sure there are other things you will run into in the process of trying to implement it.
Man Trev, I always appreciate your polite and informative posts. Now that you have so carefully walked me through this route I agree that it's tough and likely has more unseen obstacles. I think I'll try to get a GM command to execute in Perl, and if that doesn't work change the quest enough so that it is easier to implement. A lot can be done with some good SQL code.

Thanks again guys.

revloc02c
11-05-2011, 10:10 PM
Ok so I thought I knew what I was doing, but what I tried did not work. Here's what I did:

Added in file questmgr.cpp
// Expose GM command #task reloadall. revloc02 5Nov2011
void QuestManager::TaskReloadAll() {
worldserver.SendReloadTasks(RELOADTASKS);
}
Added in file questmgr.h
// Expose GM command #task reloadall. revloc02 5Nov2011
void TaskReloadAll();
Then in Perl do:
quest::TaskReloadAll();

When it gets to this line it just stops. Nothing happens.


So...

You would need to add a new function exposed to perl. It's not hard to do, and there are many examples available.

...the example I followed did not do the trick so I am wondering if lerxst or someone could please point me to an example you know of that shows how to expose a function to Perl, because I must have missed something.

lerxst2112
11-06-2011, 02:54 AM
You didn't follow the whole example. You didn't expose anything to Perl.

You'll want to find another function that has no arguments and returns nothing to copy. quest::repopzone is one.

If you search for "repopzone" you should find all you need. You need to add a function to QuestManager as you've already done, add the function that perl will call in perlparser.cpp, and add the line that lets perl know the function is available in the boot_quest function in perlparser.cpp.

revloc02c
11-07-2011, 09:57 PM
You didn't follow the whole example. You didn't expose anything to Perl.
Yup you're right, I get it now.

You'll want to find another function that has no arguments and returns nothing to copy. quest::repopzone is one.

If you search for "repopzone" you should find all you need. You need to add a function to QuestManager as you've already done, add the function that perl will call in perlparser.cpp, and add the line that lets perl know the function is available in the boot_quest function in perlparser.cpp.
Thanks again lerxst. I got it. Was having a hard time seeing it all that first time. It is exposing the function to Perl now, but unfortunately something isn't quite working right. I'll keep working with it.