Author: Chris Hilton
Foreword by SumFuka
Let's Do It...
Note: This is not a tutorial on Perl! Get the "Learning Perl"
(http://www.ora.com/catalog/lperl2/index.html) and "Programming Perl"
(http://www.ora.com/catalog/pperl2/index.html) books from O'Reilly and
Associates. These are THE books on Perl. At least basic familiarity with
programming Perl is assumed for the rest of this tutorial.
First, you'll need Perl installed on your system. If using Windows, you
must download Gurusamy Sarathy's binary version of Perl for Win32 at
ftp://ftp.digital.com/pub/plan/perl/CPAN/ports/win32/Standard/x86.
ActiveState's build of Perl for Win32 is great, but embedding Perl with
their library is vastly different from Unix builds, so we'll use
Sarathy's version for maximum portability.
Okay, now that you have Perl installed, let's look at the code for
implementing this Perl API.
InitPerl() sets everything up for us. perl_alloc and perl_construct
ready the Perl interpreter. perl_parse will parse the file 'perlq2.pl'
which should be located in the current working directory. You should put
any Perl functions or other Perl code you want initiated at startup in
this file. perl_run actually runs the code so that, at this point, your
Perl interpreter should be fully initialized. This should be added to
the InitGame() function in g_save.c, I prefer toward the end of the
function at about line 190 (+'s indicate lines added).
The next three GetPerl*() functions are intended to provide an easy
interface for retrieving scalar values from the Perl interpreter. If you
need the integer equivalent of the scalar value '$time', just use
GetPerlInt("time"). Need that as a float, you say? GetPerlFloat("time").
Never mind, you're going to print it out instead? GetPerlString("time").
Time, time, time, see what's become of me...
PerlFree() deallocates the Perl interpreter and frees up the
memory. It's always good to clean up after yourself. This should be
added to the ShutdownGame() function in g_main.c, at about line 80.
Okay, we know all the details now, let's try an example. Here's a
perlq2.pl to put in your Quake 2 directory.
Now, we need a way to call this Perl code. After adding all the
appropriate code from above for setting up the Perl interpreter, we add
a new command to g_cmds.c.
One last tidbit we need is a way to call this command function, so we
add the following to ClientCommand() in g_cmds.c.
Anyway, I think this is a pretty simple but fairly powerful API to Perl
(it seems like it's hard NOT to do something powerful with Perl).
If anyone finds this useful and would like to see me work more on it
(like, oh, adding ERROR HANDLING, maybe, figuring out what PerlEval() is
returning...), drop me a line so I'll know there's interest. Speaking of
error handling, at this point, there isn't any, so try running your Perl
scripts from the command line first if you're having problems. Also, get
Perl running with a simple script (like the above example) first before
you go wild with your 2000 lines of Perl code, okay?
Full source, patch file, and DLL at http://www.jump.net/~dctank.
This site, and all content and graphics displayed on it,
Difficulty: Medium
Actually, most of this code is program independent, so knock yourself
out imbedding Perl into all of your programs. :) If the specifics of
those functions look overwhelming, don't worry too much about them.
Mostly they refer to stuff in the Perl library that you'll never have to
bother with because you'll be using these nifty little API functions
instead. Let's go over those functions.
//
// cch_perlq2.c
//
// Adding a perl interpreter to Quake 2
#include
PerlEval() is where you will be feeding Perl code to the interpreter.
Just call PerlEval() with any string containing Perl code and the
interpreter will evaluate it. That's right, any Perl code should work.
As the perlembed manpage/documentation says, "Your string can be as long
as you wish; it can contain multiple statements; it can employ 'use',
'require', and 'do' to include external Perl files." The world is your
oyster. Perl. Get it? Anyway...
InitClientResp (&game.clients[i]);
}
globals.num_edicts = game.maxclients+1;
+
+ // CCH: Initialize perl interpreter
+ InitPerl();
}
Finally, to use all these functions, you'll have to add the following to
g_local.h.
gi.FreeTags (TAG_LEVEL);
gi.FreeTags (TAG_GAME);
+
+ // CCH: Free the perl interpreter
+ FreePerl();
}
"handy.h" is included to resolve the I32 reference that PerlEval()
returns. You'll also need to set your compiling options so that the
Perl library, perl.lib, is included in your project and the include
files can be found. In MSVC 5.0, this entails going to Tools->Options,
selecting the Directories tab, and adding your Perl CORE directory
(probably "C:\Perl\Lib\CORE") to the Include and Library directories.
You should also open your Project, go to Project->Settings, select "All
Configurations" in the "Settings For:" field, select the Link tab, then
add 'perl.lib' to the Object/library modules. Hopefully, those of you
using other compilers can figure out your own implementation, but I
recommend reading the perlembed manpage/documentation if you run into
trouble.
+// CCH: PerlQ2 includes & functions
+#include
This file contains one Perl function called CapAndColor(). It takes
a string as its first argument, capitalizes the first letter of every
word and sets that letter to print in green text in Quake 2. It then
returns this new string.
#
# perlq2.pl
#
sub CapAndColor {
my($string) = @_;
$string =~ s/\b\w/pack("c",ord(uc($&))|128)/eg;
return($string);
}
This function simply sprintf's our japh variable and the Perl code we
want to call into a temporary perlCommand buffer. Notice how we have
'$return' receive the return value from &CapAndColor(); we could call
&CapAndColor() directly, but we'd have no way to get to the result!
After having PerlEval() evaluate our command, we then use
GetPerlString() to retrieve the result of the &CapAndColor() call and
centerprint it to the player.
+/*
+=================
+Cmd_JAPH_f
+CCH: function for JAPH command
+=================
+*/
+void Cmd_JAPH_f (edict_t *ent)
+{
+ char *japh = "just another perl hacker";
+ char perlCommand[64];
+
+ sprintf(perlCommand, "$return = &CapAndColor('%s');", japh);
+ PerlEval(perlCommand);
+ gi.centerprintf(ent, "%s\n", GetPerlString("return"));
+}
Now, you should be able to compile this up, load quake2 with your new
gamex86.dll, and type 'cmd japh' at the console and see 'Just Another
Perl Hacker' centerprinted on your screen with J, A, P, and H in green,
the rest of the text in white. For you paranoid types and Intel lawyers
(I repeat myself?), this is not 'hacker' in the media sense. Just
thought I'd mention that for Randal's sake.
Cmd_Kill_f (ent);
else if (Q_stricmp (cmd, "putaway") == 0)
Cmd_PutAway_f (ent);
+
+ // CCH: japh command
+ else if (Q_stricmp (cmd, "japh") == 0)
+ Cmd_JAPH_f (ent);
+
else if (Q_stricmp (cmd, "wave") == 0)
Cmd_Wave_f (ent);
else if (Q_stricmp (cmd, "gameversion") == 0)
are ©opyrighted to the
Quake DeveLS
team. All rights received.
Got a suggestion? Comment? Question? Hate mail?
Send it
to us!
Oh yeah, this site is best viewed in 16 Bit or higher, with the resolution on
800*600.
Thanks to
Planet Quake for their great help and support with hosting.
Best viewed with Netscape
4