mirror of
https://github.com/solemnwarning/ipxwrapper
synced 2024-12-30 16:45:37 +01:00
Implemented basic DirectPlay test suite.
This commit is contained in:
parent
91c39a769f
commit
e5085ba1d9
9
Makefile
9
Makefile
@ -52,7 +52,8 @@ BIN_FILES := $(shell cat manifest.bin.txt)
|
||||
SRC_FILES := $(shell cat manifest.src.txt)
|
||||
|
||||
TOOLS := tools/socket.exe tools/list-interfaces.exe tools/bind.exe tools/ipx-send.exe \
|
||||
tools/ipx-recv.exe tools/spx-server.exe tools/spx-client.exe tools/ipx-isr.exe
|
||||
tools/ipx-recv.exe tools/spx-server.exe tools/spx-client.exe tools/ipx-isr.exe \
|
||||
tools/dptool.exe
|
||||
|
||||
# DLLs to copy to the tests directory before running the test suite.
|
||||
|
||||
@ -76,11 +77,11 @@ dist: all
|
||||
zip -r ipxwrapper-$(VERSION)-src.zip ipxwrapper-$(VERSION)-src/
|
||||
rm -r ipxwrapper-$(VERSION)-src/
|
||||
|
||||
tools: $(TOOLS) tests/addr.exe ipxwrapper.dll wsock32.dll
|
||||
cp ipxwrapper.dll wsock32.dll tools/
|
||||
tools: $(TOOLS) tests/addr.exe ipxwrapper.dll wsock32.dll dpwsockx.dll
|
||||
cp ipxwrapper.dll wsock32.dll dpwsockx.dll tools/
|
||||
|
||||
tools/%.exe: tools/%.c tools/tools.h src/addr.o
|
||||
$(CC) $(CFLAGS) -I./src/ -o $@ $< src/addr.o -lwsock32
|
||||
$(CC) $(CFLAGS) -I./src/ -o $@ $< src/addr.o -lwsock32 -lole32 -lrpcrt4
|
||||
|
||||
tests/addr.exe: tests/addr.o tests/tap/basic.o src/addr.o
|
||||
$(CC) $(CFLAGS) -I./ -o $@ $^ -lwsock32
|
||||
|
@ -18,6 +18,8 @@ Running the test suite
|
||||
|
||||
The test suite requires a Linux system and a Windows system, connected by two Ethernet networks with fixed IPv4 addresses on each. You must run `make tools` before attempting to run the test suite.
|
||||
|
||||
Running the DirectPlay tests requires an additional two Windows systems, these have the same requirements as the first, except they only need network adapters on the first network.
|
||||
|
||||
The Linux system:
|
||||
|
||||
* Must have the following Perl modules installed:
|
||||
|
416
tests/50-dplay.t
Normal file
416
tests/50-dplay.t
Normal file
@ -0,0 +1,416 @@
|
||||
# IPXWrapper test suite
|
||||
# Copyright (C) 2015 Daniel Collins <solemnwarning@solemnwarning.net>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms of the GNU General Public License version 2 as published by
|
||||
# the Free Software Foundation.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
# more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with
|
||||
# this program; if not, write to the Free Software Foundation, Inc., 51
|
||||
# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
# Number of seconds to wait for things to propagate.
|
||||
use constant PROP_TIME => 3;
|
||||
|
||||
use Test::Spec;
|
||||
|
||||
use FindBin;
|
||||
use lib "$FindBin::Bin/lib/";
|
||||
|
||||
use IPXWrapper::Tool::DPTool;
|
||||
|
||||
require "$FindBin::Bin/config.pm";
|
||||
our ($remote_ip_a, $remote_b_ip, $remote_c_ip);
|
||||
|
||||
# Nicked from dplay.h
|
||||
use constant DPID_ALLPLAYERS => 0;
|
||||
|
||||
describe "A single DirectPlay client" => sub
|
||||
{
|
||||
it "can find a published session" => sub
|
||||
{
|
||||
my $host = IPXWrapper::Tool::DPTool->new($remote_b_ip);
|
||||
my $s_guid = $host->create_session("catarrhous");
|
||||
|
||||
my $client = IPXWrapper::Tool::DPTool->new($remote_ip_a);
|
||||
my %sessions = $client->list_sessions();
|
||||
|
||||
cmp_deeply(\%sessions, {
|
||||
$s_guid => "catarrhous",
|
||||
});
|
||||
};
|
||||
|
||||
it "can find concurrently published sessions" => sub
|
||||
{
|
||||
my $host_a = IPXWrapper::Tool::DPTool->new($remote_b_ip);
|
||||
my $sa_guid = $host_a->create_session("sturt");
|
||||
|
||||
my $host_b = IPXWrapper::Tool::DPTool->new($remote_c_ip);
|
||||
my $sb_guid = $host_b->create_session("duodena");
|
||||
|
||||
my $client = IPXWrapper::Tool::DPTool->new($remote_ip_a);
|
||||
my %sessions = $client->list_sessions();
|
||||
|
||||
cmp_deeply(\%sessions, {
|
||||
$sa_guid => "sturt",
|
||||
$sb_guid => "duodena",
|
||||
});
|
||||
};
|
||||
|
||||
my $single_client_common_init = sub
|
||||
{
|
||||
my $host = IPXWrapper::Tool::DPTool->new($remote_b_ip);
|
||||
my $s_guid = $host->create_session("chaya");
|
||||
|
||||
my $client = IPXWrapper::Tool::DPTool->new($remote_ip_a);
|
||||
$client->list_sessions();
|
||||
$client->join_session($s_guid);
|
||||
|
||||
return ($host, $client);
|
||||
};
|
||||
|
||||
it "can join a published session" => sub
|
||||
{
|
||||
my ($host, $client) = $single_client_common_init->();
|
||||
|
||||
pass(); # We didn't die!
|
||||
};
|
||||
|
||||
it "can see players on the host" => sub
|
||||
{
|
||||
my ($host, $client) = $single_client_common_init->();
|
||||
|
||||
my $host_player = $host->create_player("host");
|
||||
my %client_players = $client->list_players();
|
||||
|
||||
cmp_deeply(\%client_players, {
|
||||
$host_player => "host",
|
||||
});
|
||||
};
|
||||
|
||||
it "can create players visible to the host" => sub
|
||||
{
|
||||
my ($host, $client) = $single_client_common_init->();
|
||||
|
||||
my $client_player = $client->create_player("client");
|
||||
my %host_players = $host->list_players();
|
||||
|
||||
cmp_deeply(\%host_players, {
|
||||
$client_player => "client",
|
||||
});
|
||||
};
|
||||
|
||||
it "can receive a message from the host" => sub
|
||||
{
|
||||
my ($host, $client) = $single_client_common_init->();
|
||||
|
||||
my $host_player = $host->create_player("host");
|
||||
my $client_player = $client->create_player("client");
|
||||
|
||||
$host->send_message($host_player, $client_player, "foreordainment");
|
||||
|
||||
sleep(PROP_TIME);
|
||||
|
||||
$client->exit();
|
||||
$host->exit();
|
||||
|
||||
my @host_messages = $host->messages();
|
||||
my @client_messages = $client->messages();
|
||||
|
||||
cmp_deeply(\@host_messages, []);
|
||||
|
||||
cmp_deeply(\@client_messages, [
|
||||
{
|
||||
from => $host_player,
|
||||
to => $client_player,
|
||||
message => "foreordainment",
|
||||
},
|
||||
]);
|
||||
};
|
||||
|
||||
it "can send messages to the host" => sub
|
||||
{
|
||||
my ($host, $client) = $single_client_common_init->();
|
||||
|
||||
my $host_player = $host->create_player("host");
|
||||
my $client_player = $client->create_player("client");
|
||||
|
||||
$client->send_message($client_player, $host_player, "iskenderun");
|
||||
|
||||
sleep(PROP_TIME);
|
||||
|
||||
$client->exit();
|
||||
$host->exit();
|
||||
|
||||
my @host_messages = $host->messages();
|
||||
my @client_messages = $client->messages();
|
||||
|
||||
cmp_deeply(\@host_messages, [
|
||||
{
|
||||
from => $client_player,
|
||||
to => $host_player,
|
||||
message => "iskenderun",
|
||||
},
|
||||
]);
|
||||
|
||||
cmp_deeply(\@client_messages, []);
|
||||
};
|
||||
};
|
||||
|
||||
describe "Concurrent DirectPlay clients" => sub
|
||||
{
|
||||
my $multi_client_common_init = sub
|
||||
{
|
||||
my $host = IPXWrapper::Tool::DPTool->new($remote_ip_a);
|
||||
my $s_guid = $host->create_session("meninges");
|
||||
|
||||
my $client_a = IPXWrapper::Tool::DPTool->new($remote_b_ip);
|
||||
$client_a->list_sessions();
|
||||
$client_a->join_session($s_guid);
|
||||
|
||||
my $client_b = IPXWrapper::Tool::DPTool->new($remote_c_ip);
|
||||
$client_b->list_sessions();
|
||||
$client_b->join_session($s_guid);
|
||||
|
||||
return ($host, $client_a, $client_b);
|
||||
};
|
||||
|
||||
they "can join a published session" => sub
|
||||
{
|
||||
my ($host, $client_a, $client_b) = $multi_client_common_init->();
|
||||
|
||||
pass(); # Didn't die!
|
||||
};
|
||||
|
||||
they "can see players on the host" => sub
|
||||
{
|
||||
my ($host, $client_a, $client_b) = $multi_client_common_init->();
|
||||
|
||||
my $host_player = $host->create_player("host");
|
||||
|
||||
sleep(PROP_TIME);
|
||||
|
||||
my %ca_players = $client_a->list_players();
|
||||
my %cb_players = $client_b->list_players();
|
||||
|
||||
cmp_deeply(\%ca_players, { $host_player => "host" });
|
||||
cmp_deeply(\%cb_players, { $host_player => "host" });
|
||||
};
|
||||
|
||||
they "can create players visible to the host" => sub
|
||||
{
|
||||
my ($host, $client_a, $client_b) = $multi_client_common_init->();
|
||||
|
||||
my $ca_player = $client_a->create_player("client_a");
|
||||
my $cb_player = $client_b->create_player("client_b");
|
||||
|
||||
sleep(PROP_TIME);
|
||||
|
||||
my %host_players = $host->list_players();
|
||||
|
||||
cmp_deeply(\%host_players, {
|
||||
$ca_player => "client_a",
|
||||
$cb_player => "client_b",
|
||||
});
|
||||
};
|
||||
|
||||
they "can create players visible to each other" => sub
|
||||
{
|
||||
my ($host, $client_a, $client_b) = $multi_client_common_init->();
|
||||
|
||||
my $ca_player = $client_a->create_player("client_a");
|
||||
my $cb_player = $client_b->create_player("client_b");
|
||||
|
||||
sleep(PROP_TIME);
|
||||
|
||||
my %ca_players = $client_a->list_players();
|
||||
my %cb_players = $client_b->list_players();
|
||||
|
||||
my %expect_players = (
|
||||
$ca_player => "client_a",
|
||||
$cb_player => "client_b",
|
||||
);
|
||||
|
||||
cmp_deeply(\%ca_players, \%expect_players);
|
||||
cmp_deeply(\%cb_players, \%expect_players);
|
||||
};
|
||||
|
||||
they "can receive messages from the host" => sub
|
||||
{
|
||||
my ($host, $client_a, $client_b) = $multi_client_common_init->();
|
||||
|
||||
my $host_player = $host->create_player("host");
|
||||
my $ca_player = $client_a->create_player("client_a");
|
||||
my $cb_player = $client_b->create_player("client_b");
|
||||
|
||||
$host->send_message($host_player, $ca_player, "myrmecophilous");
|
||||
$host->send_message($host_player, $cb_player, "indigestibly");
|
||||
sleep(PROP_TIME);
|
||||
|
||||
$client_b->exit();
|
||||
$client_a->exit();
|
||||
$host->exit();
|
||||
|
||||
my @host_messages = $host->messages();
|
||||
my @ca_messages = $client_a->messages();
|
||||
my @cb_messages = $client_b->messages();
|
||||
|
||||
cmp_deeply(\@host_messages, []);
|
||||
|
||||
cmp_deeply(\@ca_messages, [
|
||||
{
|
||||
from => $host_player,
|
||||
to => $ca_player,
|
||||
message => "myrmecophilous",
|
||||
},
|
||||
]);
|
||||
|
||||
cmp_deeply(\@cb_messages, [
|
||||
{
|
||||
from => $host_player,
|
||||
to => $cb_player,
|
||||
message => "indigestibly",
|
||||
},
|
||||
]);
|
||||
};
|
||||
|
||||
they "can send messages to the host" => sub
|
||||
{
|
||||
my ($host, $client_a, $client_b) = $multi_client_common_init->();
|
||||
|
||||
my $host_player = $host->create_player("host");
|
||||
my $ca_player = $client_a->create_player("client_a");
|
||||
my $cb_player = $client_b->create_player("client_b");
|
||||
|
||||
$client_a->send_message($ca_player, $host_player, "unvivid");
|
||||
sleep(PROP_TIME);
|
||||
|
||||
$client_b->send_message($cb_player, $host_player, "matrilateral");
|
||||
sleep(PROP_TIME);
|
||||
|
||||
$client_b->exit();
|
||||
$client_a->exit();
|
||||
$host->exit();
|
||||
|
||||
my @host_messages = $host->messages();
|
||||
my @ca_messages = $client_a->messages();
|
||||
my @cb_messages = $client_b->messages();
|
||||
|
||||
cmp_deeply(\@host_messages, [
|
||||
{
|
||||
from => $ca_player,
|
||||
to => $host_player,
|
||||
message => "unvivid",
|
||||
},
|
||||
{
|
||||
from => $cb_player,
|
||||
to => $host_player,
|
||||
message => "matrilateral",
|
||||
},
|
||||
]);
|
||||
|
||||
cmp_deeply(\@ca_messages, []);
|
||||
cmp_deeply(\@cb_messages, []);
|
||||
};
|
||||
|
||||
they "can send messages to each other" => sub
|
||||
{
|
||||
my ($host, $client_a, $client_b) = $multi_client_common_init->();
|
||||
|
||||
my $host_player = $host->create_player("host");
|
||||
my $ca_player = $client_a->create_player("client_a");
|
||||
my $cb_player = $client_b->create_player("client_b");
|
||||
|
||||
$client_a->send_message($ca_player, $cb_player, "postpuberty");
|
||||
$client_b->send_message($cb_player, $ca_player, "veridic");
|
||||
sleep(PROP_TIME);
|
||||
|
||||
$client_b->exit();
|
||||
$client_a->exit();
|
||||
$host->exit();
|
||||
|
||||
my @host_messages = $host->messages();
|
||||
my @ca_messages = $client_a->messages();
|
||||
my @cb_messages = $client_b->messages();
|
||||
|
||||
cmp_deeply(\@host_messages, []);
|
||||
|
||||
cmp_deeply(\@ca_messages, [
|
||||
{
|
||||
from => $cb_player,
|
||||
to => $ca_player,
|
||||
message => "veridic",
|
||||
},
|
||||
]);
|
||||
|
||||
cmp_deeply(\@cb_messages, [
|
||||
{
|
||||
from => $ca_player,
|
||||
to => $cb_player,
|
||||
message => "postpuberty",
|
||||
},
|
||||
]);
|
||||
};
|
||||
|
||||
they "can send a message to all players" => sub
|
||||
{
|
||||
my ($host, $client_a, $client_b) = $multi_client_common_init->();
|
||||
|
||||
my $host_player = $host->create_player("host");
|
||||
my $ca_player = $client_a->create_player("client_a");
|
||||
my $cb_player = $client_b->create_player("client_b");
|
||||
|
||||
$client_a->send_message($ca_player, DPID_ALLPLAYERS, "kendrew");
|
||||
sleep(PROP_TIME);
|
||||
|
||||
$client_b->send_message($cb_player, DPID_ALLPLAYERS, "derivation");
|
||||
sleep(PROP_TIME);
|
||||
|
||||
$client_b->exit();
|
||||
$client_a->exit();
|
||||
$host->exit();
|
||||
|
||||
my @host_messages = $host->messages();
|
||||
my @ca_messages = $client_a->messages();
|
||||
my @cb_messages = $client_b->messages();
|
||||
|
||||
cmp_deeply(\@host_messages, [
|
||||
{
|
||||
from => $ca_player,
|
||||
to => $host_player,
|
||||
message => "kendrew",
|
||||
},
|
||||
{
|
||||
from => $cb_player,
|
||||
to => $host_player,
|
||||
message => "derivation",
|
||||
},
|
||||
]);
|
||||
|
||||
cmp_deeply(\@ca_messages, [
|
||||
{
|
||||
from => $cb_player,
|
||||
to => $ca_player,
|
||||
message => "derivation",
|
||||
},
|
||||
]);
|
||||
|
||||
cmp_deeply(\@cb_messages, [
|
||||
{
|
||||
from => $ca_player,
|
||||
to => $cb_player,
|
||||
message => "kendrew",
|
||||
},
|
||||
]);
|
||||
};
|
||||
};
|
||||
|
||||
runtests unless caller;
|
@ -19,6 +19,11 @@ our $remote_ip_a = "172.16.1.21";
|
||||
our $remote_mac_b = "08:00:27:43:47:5C";
|
||||
our $remote_ip_b = "172.16.2.21";
|
||||
|
||||
# IP addresses of additional nodes used for testing DirectPlay.
|
||||
|
||||
our $remote_b_ip = "172.16.1.22";
|
||||
our $remote_c_ip = "172.16.1.23";
|
||||
|
||||
# Network broadcast IPs.
|
||||
|
||||
our $net_a_bcast = "172.16.1.255";
|
||||
|
176
tests/lib/IPXWrapper/Tool/DPTool.pm
Normal file
176
tests/lib/IPXWrapper/Tool/DPTool.pm
Normal file
@ -0,0 +1,176 @@
|
||||
# IPXWrapper test suite
|
||||
# Copyright (C) 2015 Daniel Collins <solemnwarning@solemnwarning.net>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms of the GNU General Public License version 2 as published by
|
||||
# the Free Software Foundation.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
# more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with
|
||||
# this program; if not, write to the Free Software Foundation, Inc., 51
|
||||
# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
package IPXWrapper::Tool::DPTool;
|
||||
|
||||
use Carp;
|
||||
use IPC::Open3;
|
||||
use POSIX qw(:signal_h);
|
||||
use Test::Spec;
|
||||
|
||||
sub new
|
||||
{
|
||||
my ($class, $host_ip) = @_;
|
||||
|
||||
my @command = ("ssh", $host_ip, "Z:\\tools\\dptool.exe");
|
||||
note(join(" ", @command));
|
||||
|
||||
# No need for error checking here - open3 throws on failure.
|
||||
my $pid = open3(my $in, my $out, undef, @command);
|
||||
|
||||
my $self = bless({
|
||||
pid => $pid,
|
||||
in => $in,
|
||||
out => $out,
|
||||
|
||||
messages => [],
|
||||
}, $class);
|
||||
|
||||
my $output = "";
|
||||
|
||||
while(defined(my $line = <$out>))
|
||||
{
|
||||
$output .= $line;
|
||||
|
||||
$line =~ s/[\r\n]//g;
|
||||
|
||||
if($line eq "ready")
|
||||
{
|
||||
return $self;
|
||||
}
|
||||
}
|
||||
|
||||
die("Unexpected output from dptool.exe:\n$output");
|
||||
}
|
||||
|
||||
sub DESTROY
|
||||
{
|
||||
my ($self) = @_;
|
||||
|
||||
kill(SIGKILL, $self->{pid});
|
||||
waitpid($self->{pid}, 0);
|
||||
}
|
||||
|
||||
sub _do_cmd
|
||||
{
|
||||
my ($self, $cmd, $expect) = @_;
|
||||
|
||||
my $stdin = $self->{in};
|
||||
print {$stdin} join(" ", @$cmd), "\n";
|
||||
|
||||
my $out = $self->{out};
|
||||
my $output = "";
|
||||
|
||||
while(defined(my $line = <$out>))
|
||||
{
|
||||
$line =~ s/\r//g;
|
||||
|
||||
if($line =~ m/^ready$/m)
|
||||
{
|
||||
last;
|
||||
}
|
||||
|
||||
if($line =~ m/^message (\d+) (\d+) (.*)$/)
|
||||
{
|
||||
push(@{ $self->{messages} }, {
|
||||
from => $1,
|
||||
to => $2,
|
||||
message => $3,
|
||||
});
|
||||
}
|
||||
else{
|
||||
$output .= $line;
|
||||
}
|
||||
}
|
||||
|
||||
if($output !~ $expect)
|
||||
{
|
||||
confess("Unexpected output from dptool.exe: $output");
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
sub create_session
|
||||
{
|
||||
my ($self, $session_name) = @_;
|
||||
|
||||
my $output = $self->_do_cmd([ "create_session", $session_name ], qr/^session_guid \S+$/);
|
||||
|
||||
my ($session_guid) = ($output =~ m/ (\S+)$/);
|
||||
return $session_guid;
|
||||
}
|
||||
|
||||
sub list_sessions
|
||||
{
|
||||
my ($self) = @_;
|
||||
|
||||
my $output = $self->_do_cmd([ "list_sessions" ], qr/^(?:session \S+ \S*\n)*$/);
|
||||
|
||||
my %sessions = map { m/(\S+) (\S*)$/; ($1 => $2) } ($output =~ m/(.+)/g);
|
||||
return %sessions;
|
||||
}
|
||||
|
||||
sub join_session
|
||||
{
|
||||
my ($self, $session_guid) = @_;
|
||||
|
||||
$self->_do_cmd([ "join_session", $session_guid ], qr/^$/);
|
||||
}
|
||||
|
||||
sub create_player
|
||||
{
|
||||
my ($self, $player_longname) = @_;
|
||||
|
||||
my $output = $self->_do_cmd([ "create_player", $player_longname ], qr/^player_id [0-9]+$/);
|
||||
|
||||
my ($player_id) = ($output =~ m/([0-9]+)/);
|
||||
return $player_id;
|
||||
}
|
||||
|
||||
sub list_players
|
||||
{
|
||||
my ($self) = @_;
|
||||
|
||||
my $output = $self->_do_cmd([ "list_players" ], qr/^(?:player [0-9]+ (\S+)\n)*$/);
|
||||
|
||||
my %sessions = map { m/(\S+) (\S*)$/ } ($output =~ m/(.+)/g);
|
||||
return %sessions;
|
||||
}
|
||||
|
||||
sub send_message
|
||||
{
|
||||
my ($self, $player_from, $player_to, $message) = @_;
|
||||
|
||||
$self->_do_cmd([ "send_message", $player_from, $player_to, $message ], qr/^$/);
|
||||
}
|
||||
|
||||
sub exit
|
||||
{
|
||||
my ($self) = @_;
|
||||
|
||||
$self->_do_cmd([ "exit" ], qr/^$/);
|
||||
}
|
||||
|
||||
sub messages
|
||||
{
|
||||
return @{ (shift)->{messages} };
|
||||
}
|
||||
|
||||
1;
|
316
tools/dptool.c
Normal file
316
tools/dptool.c
Normal file
@ -0,0 +1,316 @@
|
||||
/* IPXWrapper test tools
|
||||
* Copyright (C) 2015 Daniel Collins <solemnwarning@solemnwarning.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published by
|
||||
* the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc., 51
|
||||
* Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#define INITGUID
|
||||
|
||||
#include <dplay.h>
|
||||
#include <windows.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
|
||||
DEFINE_GUID(TEST_APP_GUID, 0x38bef9eb, 0x6ad3, 0x4fc9, 0x97, 0xff, 0xfc, 0x3c, 0x7a, 0x55, 0x3b, 0x29);
|
||||
|
||||
static HANDLE exit_event = NULL;
|
||||
|
||||
#define assert_dp_call(name, expr) \
|
||||
{ \
|
||||
HRESULT err = (expr); \
|
||||
if(err != DP_OK) \
|
||||
{ \
|
||||
fprintf(stderr, \
|
||||
__FILE__ ":%u: " name ": %u\n", \
|
||||
(unsigned int)(__LINE__), \
|
||||
(unsigned int)(err)); \
|
||||
exit(1); \
|
||||
} \
|
||||
}
|
||||
|
||||
static void lock_printf(const char *fmt, ...)
|
||||
{
|
||||
static CRITICAL_SECTION cs;
|
||||
static bool cs_init = false;
|
||||
|
||||
if(!cs_init)
|
||||
{
|
||||
InitializeCriticalSection(&cs);
|
||||
cs_init = true;
|
||||
}
|
||||
|
||||
EnterCriticalSection(&cs);
|
||||
|
||||
va_list argv;
|
||||
va_start(argv, fmt);
|
||||
vprintf(fmt, argv);
|
||||
va_end(argv);
|
||||
|
||||
LeaveCriticalSection(&cs);
|
||||
}
|
||||
|
||||
static BOOL FAR PASCAL copy_ipx_conn(
|
||||
LPCGUID *lpguidSP,
|
||||
LPVOID lpConnection,
|
||||
DWORD dwConnectionSize,
|
||||
LPCDPNAME lpName,
|
||||
DWORD dwFlags,
|
||||
LPVOID lpContext)
|
||||
{
|
||||
if(memcmp(lpguidSP, &DPSPGUID_IPX, sizeof(DPSPGUID_IPX)) == 0)
|
||||
{
|
||||
assert(*(void**)(lpContext) = malloc(dwConnectionSize));
|
||||
memcpy(*(void**)(lpContext), lpConnection, dwConnectionSize);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL FAR PASCAL list_sessions_cb(
|
||||
LPCDPSESSIONDESC2 lpThisSD,
|
||||
LPDWORD lpdwTimeout,
|
||||
DWORD dwFlags,
|
||||
LPVOID lpContext)
|
||||
{
|
||||
if(lpThisSD)
|
||||
{
|
||||
unsigned char *uuid_str;
|
||||
assert(UuidToString((UUID*)&(lpThisSD->guidInstance), &uuid_str) == RPC_S_OK);
|
||||
|
||||
lock_printf("session %s %s\n", uuid_str, lpThisSD->lpszSessionNameA);
|
||||
|
||||
RpcStringFree(&uuid_str);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
else{
|
||||
/* Last session detected in this pass, return FALSE to make the
|
||||
* invoking IDirectPlayX_EnumConnections call return rather than
|
||||
* searching forever.
|
||||
*/
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static BOOL FAR PASCAL list_players_cb(
|
||||
DPID dpId,
|
||||
DWORD dwPlayerType,
|
||||
LPCDPNAME lpName,
|
||||
DWORD dwFlags,
|
||||
LPVOID lpContext)
|
||||
{
|
||||
lock_printf("player %u %s\n", (unsigned int)(dpId), lpName->lpszLongName);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static DWORD WINAPI recv_thread_main(LPVOID lpParameter)
|
||||
{
|
||||
IDirectPlay4 *dp = (IDirectPlay4*)(lpParameter);
|
||||
|
||||
DWORD bufsize = 0;
|
||||
char *buf = NULL;
|
||||
|
||||
while(1)
|
||||
{
|
||||
DPID player_from, player_to;
|
||||
|
||||
HRESULT err = IDirectPlayX_Receive(dp, &player_from, &player_to, 0, buf, &bufsize);
|
||||
|
||||
if(WaitForSingleObject(exit_event, 0) == WAIT_OBJECT_0)
|
||||
{
|
||||
/* We are exiting */
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(err == DPERR_BUFFERTOOSMALL)
|
||||
{
|
||||
assert(buf = realloc(buf, bufsize));
|
||||
continue;
|
||||
}
|
||||
else if(err == DPERR_NOMESSAGES)
|
||||
{
|
||||
/* non-blocking I/O... ugh */
|
||||
Sleep(50);
|
||||
continue;
|
||||
}
|
||||
else if(err != DP_OK)
|
||||
{
|
||||
fprintf(stderr, "IDirectPlay4::Receive: %u\n", (unsigned int)(err));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if(player_from != DPID_SYSMSG)
|
||||
{
|
||||
lock_printf("message %u %u %s\n",
|
||||
(unsigned int)(player_from),
|
||||
(unsigned int)(player_to),
|
||||
buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
setbuf(stdout, NULL);
|
||||
setbuf(stderr, NULL);
|
||||
|
||||
exit_event = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
assert(exit_event);
|
||||
|
||||
{
|
||||
HRESULT err = CoInitializeEx(NULL, COINIT_MULTITHREADED);
|
||||
if(err != S_OK)
|
||||
{
|
||||
fprintf(stderr, "CoInitializeEx: %u\n", (unsigned int)(err));
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
IDirectPlay4 *dp;
|
||||
assert_dp_call("CoCreateInstance",
|
||||
CoCreateInstance(
|
||||
&CLSID_DirectPlay, NULL, CLSCTX_INPROC_SERVER, &IID_IDirectPlay4A, (void**)(&dp)));
|
||||
|
||||
{
|
||||
void *conn = NULL;
|
||||
|
||||
assert_dp_call("IDirectPlay4::EnumConnections",
|
||||
IDirectPlayX_EnumConnections(
|
||||
dp, NULL, (LPDPENUMCONNECTIONSCALLBACK)(©_ipx_conn), &conn, 0));
|
||||
|
||||
assert_dp_call("IDirectPlay4::InitializeConnection",
|
||||
IDirectPlayX_InitializeConnection(dp, conn, 0));
|
||||
|
||||
free(conn);
|
||||
}
|
||||
|
||||
HANDLE recv_thread = CreateThread(NULL, 0, &recv_thread_main, dp, 0, NULL);
|
||||
assert(recv_thread);
|
||||
|
||||
char line[1024];
|
||||
while(lock_printf("ready\n"), fgets(line, sizeof(line), stdin))
|
||||
{
|
||||
char *cmd = strtok(line, " \n");
|
||||
|
||||
if(strcmp(cmd, "create_session") == 0)
|
||||
{
|
||||
char *session_name = strtok(NULL, " \n");
|
||||
|
||||
DPSESSIONDESC2 session;
|
||||
memset(&session, 0, sizeof(session));
|
||||
|
||||
session.dwSize = sizeof(session);
|
||||
session.guidApplication = TEST_APP_GUID;
|
||||
session.lpszSessionNameA = session_name;
|
||||
|
||||
assert_dp_call("IDirectPlay4::Open",
|
||||
IDirectPlayX_Open(dp, &session, DPOPEN_CREATE));
|
||||
|
||||
DWORD bufsize = 0;
|
||||
|
||||
/* Need to call GetSessionDesc() to get the session
|
||||
* GUID as Open() doesn't fill it in.
|
||||
*/
|
||||
|
||||
assert(IDirectPlayX_GetSessionDesc(dp, NULL, &bufsize) == DPERR_BUFFERTOOSMALL);
|
||||
|
||||
DPSESSIONDESC2 *sd = malloc(bufsize);
|
||||
assert(sd);
|
||||
|
||||
assert_dp_call("IDirectPlay4::GetSessionDesc",
|
||||
IDirectPlayX_GetSessionDesc(dp, sd, &bufsize));
|
||||
|
||||
unsigned char *uuid_str;
|
||||
assert(UuidToString((UUID*)&(sd->guidInstance), &uuid_str) == RPC_S_OK);
|
||||
|
||||
lock_printf("session_guid %s\n", uuid_str);
|
||||
|
||||
RpcStringFree(&uuid_str);
|
||||
|
||||
free(sd);
|
||||
}
|
||||
else if(strcmp(cmd, "list_sessions") == 0)
|
||||
{
|
||||
DPSESSIONDESC2 session;
|
||||
memset(&session, 0, sizeof(session));
|
||||
|
||||
session.dwSize = sizeof(session);
|
||||
session.guidApplication = TEST_APP_GUID;
|
||||
|
||||
assert_dp_call("IDirectPlay4::EnumSessions",
|
||||
IDirectPlayX_EnumSessions(dp, &session, 3000, &list_sessions_cb, NULL, 0));
|
||||
}
|
||||
else if(strcmp(cmd, "join_session") == 0)
|
||||
{
|
||||
char *session_guid = strtok(NULL, " \n");
|
||||
|
||||
DPSESSIONDESC2 session;
|
||||
memset(&session, 0, sizeof(session));
|
||||
|
||||
session.dwSize = sizeof(session);
|
||||
UuidFromString((RPC_CSTR)(session_guid), &(session.guidInstance));
|
||||
|
||||
assert_dp_call("IDirectPlay4::Open",
|
||||
IDirectPlayX_Open(dp, &session, DPOPEN_JOIN));
|
||||
}
|
||||
else if(strcmp(cmd, "create_player") == 0)
|
||||
{
|
||||
DPID player_id;
|
||||
|
||||
DPNAME name;
|
||||
memset(&name, 0, sizeof(name));
|
||||
|
||||
name.dwSize = sizeof(name);
|
||||
name.lpszLongNameA = strtok(NULL, " \n");
|
||||
|
||||
assert_dp_call("IDirectPlay4::CreatePlayer",
|
||||
IDirectPlayX_CreatePlayer(dp, &player_id, &name, NULL, NULL, 0, 0));
|
||||
|
||||
lock_printf("player_id %u\n", (unsigned int)(player_id));
|
||||
}
|
||||
else if(strcmp(cmd, "list_players") == 0)
|
||||
{
|
||||
IDirectPlayX_EnumPlayers(dp, NULL, &list_players_cb, NULL, DPENUMPLAYERS_ALL);
|
||||
}
|
||||
else if(strcmp(cmd, "send_message") == 0)
|
||||
{
|
||||
DPID player_from = strtoul(strtok(NULL, " \n"), NULL, 10);
|
||||
DPID player_to = strtoul(strtok(NULL, " \n"), NULL, 10);
|
||||
char *message = strtok(NULL, " \n");
|
||||
|
||||
assert_dp_call("IDirectPlay4::Send",
|
||||
IDirectPlayX_Send(dp, player_from, player_to, 0, message, strlen(message) + 1));
|
||||
}
|
||||
else if(strcmp(cmd, "exit") == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
SetEvent(exit_event);
|
||||
WaitForSingleObject(recv_thread, INFINITE);
|
||||
|
||||
CloseHandle(recv_thread);
|
||||
CloseHandle(exit_event);
|
||||
|
||||
IDirectPlayX_Release(dp);
|
||||
|
||||
CoUninitialize();
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user