view release on metacpan or search on metacpan
lib/Acme/MUDLike.pm view on Meta::CPAN
# and then they could fight methods and use global variables as weapons!
#
# * http://zvtm.sourceforge.net/zgrviewer.html or something similar for showing the user the "map" of
# nodes/rooms/whatever made of has-a references or something.
#
# * /goto should put you inside an arbitrary object, /look should list as exits and/or items the object references contained by that object
# in other words, break away from our rigid API for inventory/room/etc.
#
# * need a black list black list, so we can re-add ourself to things that get serialized by Acme::State even though we're in %INC
#
# * need an error log viewabe by all.
#
# * eval and its output should be sent to the whole room.
#
# * Better account management.
#
# * There's code around to parse LPC and convert it to Perl. It would be neat to offer a full blown 2.4.5
# lib for people to play around in.
#
# * Acme::IRCLike would probably be more popular -- bolt an IRC server onto your app.
#
lib/Acme/MUDLike.pm view on Meta::CPAN
#
# Done:
#
# * mark/call commands should have a current object register, so you can do /call thingie whatever /next and then be calling
# into the object returned by thingie->whatever
#
# * /list (like look, but with stringified object references)
#
# * /mark <n> ... or... /mark <stringified obj ref>
#
# * messages still in duplicate when the same player logs in twice; make room's tell_object operate uniquely.
#
# * messages in triplicate because each player has three routines and is inserted into the floor three times. oops.
#
# * build the ajax.chat.js into source. -- okay, test.
#
# * eval, call
#
# * inventory's insert() method should set the insertee's environment to itself. that way, all objects have an environment.
#
# * Commands need to do $floor->tell_object or $self->tell_object rather than output directly.
lib/Acme/MUDLike.pm view on Meta::CPAN
my $staticp = sub {
# warn "staticp: url->path: ``@{[ $_[0]->url->path ]}''";
return 0 if $_[0]->url->path =~ m/\.js$/;
# warn "staticp: dynamic js handling override not engaged";
return $_[0]->url->path =~ m/\.(jpg|jpeg|gif|png|css|ico|js)$/
};
$continuity = $args{continuity} || Continuity->new(
staticp => sub { $staticp->(@_); },
callback => sub { login(@_) },
path_session => 1,
port => 2000,
%args,
);
print "Admin:\n", $continuity->adapter->daemon->url, '?admin=', $password, '&nick=', (getpwuid $<)[0], "\n";
$floor ||= Acme::MUDLike::room->new();
$players ||= Acme::MUDLike::inventory->new();
lib/Acme/MUDLike.pm view on Meta::CPAN
qq{
<html><head>
<script src="/jquery.js" type="text/javascript"></script>
<script src="/chat.js" type="text/javascript"></script>
</head><body>
};
}
sub footer { qq{</body></html>\n}; }
sub login {
my $request = shift;
#
# per-user variables
#
my $player;
# STDERR->print("debug: " . $request->request->url->path . "\n"); # XXX
lib/Acme/MUDLike.pm view on Meta::CPAN
# warn "handling chat.js XXX: ". $request->request->url->path;
$request->print(Acme::MUDLike::data->chat_js());
return;
} elsif($request->request->url->path eq '/jquery.js') {
# warn "handling jquery.js XXX: ". $request->request->url->path;
$request->print(Acme::MUDLike::data->jquery());
return;
}
#
# login
#
while(1) {
my $nick_tmp = $request->param('nick');
my $admin_tmp = $request->param('admin');
if(defined($nick_tmp) and defined($admin_tmp) and $nick_tmp =~ m/^[a-z]{2,20}$/i and $admin_tmp eq $password) {
my $nick = $nick_tmp;
$player = $players->named($nick) || $players->insert(Acme::MUDLike::player->new(name => $nick), );
$player->request = $request;
# @_ = ($player, $request,); goto &{Acme::MUDLike::player->can('command')};
$player->command($request); # doesn't return
}
# warn "trying login again XXX";
$nick_tmp ||= ''; $admin_tmp ||= '';
$nick_tmp =~ s/[^a-z]//gi; $admin_tmp =~ s/[^a-z0-9]//gi;
$request->print(
header, # $msg,
qq{
<form method="post" action="/">
<input type="text" name="nick" value="$nick_tmp"> <-- nickname<br>
<input type="password" name="admin" value="$admin_tmp"> <-- admin password<br>
<input type="submit" value="Enter"><br>
</form>
lib/Acme/MUDLike.pm view on Meta::CPAN
}
sub header () { Acme::MUDLike::header() }
sub footer () { Acme::MUDLike::footer() }
sub command {
my $self = shift;
my $request = shift;
# this is called by login() immediately after verifying credientials
if($request->request->url->path =~ m/pushstream/) {
# warn "pushstream path_session handling XXX";
my $w = Coro::Event->var(var => \$got_message, poll => 'w');
while(1) {
$w->next;
# warn "got_message diddled XXX";
# on submitting the form without a JS background post, the poll HTTP connection gets broken
$SIG{PIPE} = 'IGNORE';
$request->print( join "<br>\n", map { s{<}{\<}gs; s{\n}{<br>\n}g; $_ } $self->get_messages );
lib/Acme/MUDLike.pm view on Meta::CPAN
while(1) {
$request->print(header);
#
# chat/commands
#
if($request->param('action') and $request->param('action') eq 'chat') {
# chat messages first so they appear in the log below
# there's only one action defined right now -- chat. everything else hangs off of that.
my $msg = $request->param('message');
$self->parse_command($msg);
};
do {
$request->print(qq{
<b>Chat/Command:</b>
<form method="post" id="f" action="/">
<input type="hidden" name="action" value="chat">
<input type="hidden" id="nick" name="nick" value="@{[ $self->name ]}">
<input type="hidden" id="admin" name="admin" value="$password">
<input type="text" id="message" name="message" size="50">
<!-- <input type="submit" name="sendbutton" value="Send" id="sendbutton"> -->
<input type="submit" name="sendbutton" value="Send" id="sendbutton">
<span id="status"></span>
</form>
<br>
<div id="log">@{[ $self->get_html_messages ]}</div>
});
};
} continue {
$request->print(footer);
$request->next();
} # end while
}
sub parse_command {
lib/Acme/MUDLike.pm view on Meta::CPAN
Multi user chat, general purpose object tracer, eval, and give/drop/take/clone/dest/look.
Adds a social element to software development and develop it from within.
Chat within the application, eval code inside of it (sort of like a simple Read-Eval-Parse Loop).
Call methods in objects from the command line.
Create instances of objects, give them to people, drop them on the floor.
The idea is take the simple command line interface and extend it with more commands,
and to create tools and helper objects that inspect and modify the running program from within.
It fires up a Continuity/HTTP::Daemon based Web server on port 2000 and prints out a login
URL on the command line.
Paste the URL into your browser.
Chat with other users logged into the app.
Messages beginning with a slash, C</>, are interpreted as commands:
=over 2
=item C<< /look >>
See who else and what else is in the room.
=item C<< /mark >>
/mark 1
/mark torch
/mark foo::bar
/mark 0x812ea54
Select an object as the logical current object by name, package name, number (as a position in your
inventory list, which is useful for when you've cloned an object that does not define an C<id> or C<name> function),
or by memory address (as in C<< Foo::Bar=HASH(0x812ea54) >>).
=item C<< /call >>
Call a function in an object; eg, if you're holding a C<toaster>, you can write:
/call toaster add_bread 1
The special name "current" refers to the current object, as marked with mark.
lib/Acme/MUDLike.pm view on Meta::CPAN
C<apply>, and C<contents> methods.
C<< $self->environment >> is also an C<< Acme::MUDLike::inventory >> object holding you and other players
and objects in the room.
The environment and players in it all have C<tell_object> methods that takes a string to add to their
message buffer.
Calling C<tell_object> in the environment sends the message to all players.
Objects define various other methods.
=item C<< /who >>
List of who is logged in. Currently the same C</look>.
=item C<< /inventory >>
Or C</i> or C</inv>. Lists the items you are carrying.
=item C<< /clone >>
Creates an instance of an object given a package name. Eg:
/clone sword
lib/Acme/MUDLike.pm view on Meta::CPAN
=item C<port>
Optional. Defaults to C<2000>.
This and other parameters, such as those documented in L<Continuity>, are passed through
to C<< Continuity->new() >>.
=item C<password>
Optional. Password to use.
Everyone gets the same password, and anyone with the password can log in with any name.
Otherwise one is pseudo-randomly generated and printed to C<stdout>.
=cut
=head1 HISTORY
=over 8
=item 0.01
lib/Acme/MUDLike.pm view on Meta::CPAN
=item L<Continuity::Monitor>
=item L<Acme::State>
=item L<Acme::SubstituteSubs>
L<Acme::State> preserves state across runs and L<Acme::SubstituteSubs>.
These three modules work on their own but are complimentary to each other.
Using L<Acme::SubstituteSubs>, the program can be modified in-place without being restarted,
so you don't have to log back in again after each change batch of changes to the code.
Code changes take effect immediately.
L<Acme::State> persists variable values when the program is finally stopped and restarted.
L<Acme::State> will also optionally serialize code references to disc, so you can
C<eval> subs into existance and let it save them to disc for you and then later
use L<B::Deparse> to retrieve a version of the source.
The C<Todo> comments near the top of the source.
=head1 AUTHOR
lib/Acme/MUDLike.pm view on Meta::CPAN
function poll_server() {
var nick = document.getElementById("nick").value;
var admin = document.getElementById("admin").value;
document.getElementById('status').innerHTML = 'Polling ('+(poll_count++)+')...';
do_request('/pushstream/?nick=' + nick + '&admin=' + admin, got_update);
}
function got_update(txt) {
document.getElementById('status').innerHTML = 'Got update.'
if(document.getElementById("log").innerHTML != txt)
document.getElementById("log").innerHTML = txt;
setup_poll();
}
// This stuff gets executed once the document is loaded
$(function(){
// Start up the long-pull cycle
setup_poll();
// Unobtrusively make submitting a message use send_message()
$('#f').submit(send_message);
});
// We also send messages using AJAX
function send_message() {
var nick = $('#nick').val();
var admin = $('#admin').val();
var message = $('#message').val();
$('#log').load('/sendmessage', {
nick: nick,
admin: admin,
action: 'ajaxchat',
message: message
}, function() {
$('#message').val('');
$('#message').focus();
});
return false;
}
lib/Acme/MUDLike.pm view on Meta::CPAN
};
});
// If Mozilla is used
if ( jQuery.browser.mozilla || jQuery.browser.opera )
// Use the handy event callback
document.addEventListener( "DOMContentLoaded", jQuery.ready, false );
// If IE is used, use the excellent hack by Matthias Miller
// http://www.outofhanwell.com/blog/index.php?title=the_window_onload_problem_revisited
else if ( jQuery.browser.msie ) {
// Only works if you document.write() it
document.write("<scr" + "ipt id=__ie_init defer=true " +
"src=//:><\/script>");
// Use the defer script hack
var script = document.getElementById("__ie_init");
// script does not exist if jQuery is loaded dynamically