use strict; use warnings; package POE::Component::Minecraft; # ABSTRACT: Implements login and hooks for interacting with Minecraft use POE; use POE::Component::Client::TCP; use POE::Component::Client::HTTP; use HTTP::Request; our $client_version = 12; # current launcher version as of October 23rd 2011, package variable to allow monkey patching to new required versions sub new { my $class = shift; my $self = bless { }, $class; my %opts = (username => undef, password => undef, server => "minecraft.net", port => 25565, alias => "Minecraft", _session => undef, _http => undef, _tcp => undef, ); if (@_ > 1) { # do we have more than one argument left? %opts = @_; # we're given a hash if so } elsif (@_ == 1) { # exactly one, must be a hashref %opts = %$_[0]; } $alias = "resolver" unless $alias; $self->{_session} = POE::Session->create( object_states => [ $self => { _start => "_poe_start", login => "_poe_login", login_response => "_poe_login_response", }, ], ); $self->{_http} = POE::Component::Client::HTTP->spawn( Agent => 'Minecraft Bot 2387', # defaults to something long Alias => 'ua', # defaults to 'weeble' From => 'simcop2387@simcop2387.info', # defaults to undef (no header) Timeout => 60, # defaults to 180 seconds FollowRedirects => 2, # defaults to 0 (off) ); return $self; } sub _poe_start { my $self = $_[OBJECT]; $_[KERNEL]->alias_set("$_[OBJECT]"); } sub _poe_login { my $self = $_[OBJECT]; my $to = $self->{_http}->ID(); my $req = HTTP::Request->new(POST => 'https://login.minecraft.net/'); $req->content_type('application/x-www-form-urlencoded'); $req->content('user='.$self->{username}.'&password='.$self->{password}.'&version='.$client_version); $poe_kernel->post($to, 'request', 'login_response', $req); } sub _poe_login_response { my $self = $_[OBJECT]; my ($req, $res) = @_[ARG0, ARG1]; if ($res->is_success) { print $res->content, "\n"; @{$self}{qw/game_version download_ticket username_case session_id/} = split /:/, $res->content; $poe_kernel->post($_[SESSION], "connect"); } else { # i should have some way of giving this back to the user of the module, probably through the callback # $cb->(LOGIN_FAILED, $res->status_line) print $res->status_line, "\n"; } } sub _poe_connect { my $self = $_[OBJECT]; die "Not going to actually connect!" } # strict object methods, these are directly called by the user of the module sub login { my $self = shift; my $cb = shift; $self->{_login_cb} = $cb; # save login callback if (defined($self->{username}) && defined($self->{password}) && defined($self->{server}) && defined($self->{port})) { $poe_kernel->call($_[SESSION], login); } else { die "No Server or credentials"; } } # ACCESORS sub credentials { my ($self, $username, $password) = @_; $self->{username} = $username if (defined($username)); $self->{password} = $password if (defined($password)); return [$self->{username}, $self->{password}]; # send them back so that this is an accessor also } sub server { my ($self, $server) = @_; $self->{server} = $server if (defined $server); return $self->{server}; } sub port { my ($self, $port) = @_; $self->{port} = $port if defined($port); return $self->{port}; } 1;