From 73f832badf8125bc366ea1f4fd6b3c2c621d3ce2 Mon Sep 17 00:00:00 2001 From: JT Smith Date: Thu, 22 Jul 2010 14:39:20 -0500 Subject: [PATCH] added alliances --- docs/Embassy.pod | 277 +++++++++++++++++++++- docs/Inbox.pod | 11 +- lib/Lacuna/DB/Result/Alliance.pm | 152 ++++++++++++ lib/Lacuna/DB/Result/AllianceInvite.pm | 25 ++ lib/Lacuna/DB/Result/Building/Embassy.pm | 183 ++++++++++++++ lib/Lacuna/DB/Result/Empire.pm | 26 +- lib/Lacuna/DB/Result/Map/Body/Planet.pm | 57 +++-- lib/Lacuna/DB/Result/Probes.pm | 8 +- lib/Lacuna/RPC/Building/Embassy.pm | 161 +++++++++++++ lib/Lacuna/RPC/Building/Observatory.pm | 2 +- lib/Lacuna/RPC/Inbox.pm | 27 ++- t/Embassy.t | 61 +++++ t/Inbox.t | 4 +- var/messages/alliance_accept.txt | 7 + var/messages/alliance_invite.txt | 5 + var/messages/alliance_leave.txt | 7 + var/messages/alliance_reject.txt | 7 + var/messages/alliance_withdraw_invite.txt | 5 + 18 files changed, 979 insertions(+), 46 deletions(-) create mode 100644 lib/Lacuna/DB/Result/Alliance.pm create mode 100644 lib/Lacuna/DB/Result/AllianceInvite.pm create mode 100644 t/Embassy.t create mode 100644 var/messages/alliance_accept.txt create mode 100644 var/messages/alliance_invite.txt create mode 100644 var/messages/alliance_leave.txt create mode 100644 var/messages/alliance_reject.txt create mode 100644 var/messages/alliance_withdraw_invite.txt diff --git a/docs/Embassy.pod b/docs/Embassy.pod index b29b6ad9..37218d0b 100644 --- a/docs/Embassy.pod +++ b/docs/Embassy.pod @@ -1,7 +1,282 @@ =head1 Embassy Methods -Embassy is accessible via the URL C. +Embassy is accessible via the URL C. The embassy is used to form alliances with other players. The list of methods below represents changes and additions to the methods that all L share. + +=head2 create_alliance ( session_id, building_id, name ) + +Create a new alliance. Returns the same output as C. + +=head3 session_id + +A session id. + +=head3 building_id + +The unique id of the embassy. + +=head3 name + +A unique name for this alliance. Must be between 3 and 30 characters, cannot contain profanity or restricted characters. + + + + +=head2 dissolve_alliance ( session_id, building_id ) + +Can only be called by alliance leader. Disbands and existing alliance. + + { + "status" : { ... } + } + +=head3 session_id + +A session id. + +=head3 building_id + +The unique id of the embassy. + + + +=head2 get_alliance_status ( session_id, building_id ) + +Returns everything about an alliance that members should know. + + { + "id" : "id-goes-here", + "name" : "United Federation of Planets", + "members" : [ + { + "empire_id" : "id goes here", + "name" : "Klingons", + } + ... + ], + "leader_id" : "id goes here", + "forum_uri" : "http://forum.example.com/", + "description" : "This is public information.", + "announcements" : "This is private information.", + "date_created" : "01 31 2010 13:09:05 +0600" + } + +=head3 session_id + +A session id. + +=head3 building_id + +The unique id of the embassy. + + +=head2 send_invite ( session_id, building_id, invitee_id, [ message ] ) + +Can only be called by alliance leader. Invite an empire to an alliance. + + { + "status" : { ... } + } + +=head3 session_id + +A session id. + +=head3 building_id + +The unique id of the embassy. + +=head3 invitee_id + +The unique id of an empire you'd like to invite to an alliance. See L to look up empire ids. + +=head3 message + +Optional. A personalized welcome message that will be included in the invitation. + + + +=head3 withdraw_invite ( session_id, building_id, invite_id, [ message ] ) + +Can only be called by alliance leader. Delete an invitation. + + { + "status" : { ... } + } + +=head3 session_id + +A session id. + +=head3 building_id + +The unique id of the embassy. + +=head3 invite_id + +The unique id of an invitation. See C for details. + +=head3 message + +Optional. A personalized message that will be sent to the user about why their invitation has been withdrawn. + + + +=head3 accept_invite ( session_id, building_id, invite_id, [ message ] ) + +Accept an invitation. Returns the same output as C. + +=head3 session_id + +A session id. + +=head3 building_id + +The unique id of the embassy. + +=head3 invite_id + +The unique id of an invitation. See C for details. + +=head3 message + +Optional. A personalized message that will be sent to the alliance leader with your acceptance. + + + +=head3 reject_invite ( session_id, building_id, invite_id, [ message ] ) + +Delete an invitation. + + { + "status" : { ... } + } + +=head3 session_id + +A session id. + +=head3 building_id + +The unique id of the embassy. + +=head3 invite_id + +The unique id of an invitation. See C for details. + +=head3 message + +Optional. A personalized message that will be sent to the alliance leader along with your rejection. + + + + +=head3 get_pending_invites ( session_id, building_id ) + +Can only be called by the alliance leader. Returns a list of invitations that have been sent out, but that have not been accepted, rejected, or withdrawn. + + { + "status" : { ... }, + "invites" : [ + { + "id" : "id-goes-here", + "name" : "The Borg", + "empire_id" : "id-goes-here" + }, + ... + ] + } + +=head3 session_id + +A session id. + +=head3 building_id + +The unique id of the embassy. + + + + +=head3 get_my_invites ( session_id, building_id ) + +Returns a list of invitations that have been offered to this empire. + + { + "status" : { ... }, + "invites" : [ + { + "id" : "id-goes-here", + "name" : "United Federation of Planets", + "alliance_id" : "id-goes-here" + }, + ... + ] + } + +=head3 session_id + +A session id. + +=head3 building_id + +The unique id of the embassy. + + + +=head3 assign_alliance_leader ( session_id, building_id, new_leader_id ) + +Sets a new empire to lead the alliance. Can only be called by the current alliance leader. Returns the same thing as C. + +=head3 session_id + +A session id. + +=head3 building_id + +The unique id of the embassy. + +=head3 new_leader_id + +The unique id of an empire that will lead the alliance going forward. The empire must already be a member of the alliance. + + + +=head2 update_alliance ( session_id, building_id, params ) + +Updates the properties of an alliance. Returns the same thing as C. Can only be called by the alliance leader. + +=head3 session_id + +A session id. + +=head3 building_id + +The unique id of the embassy. + +=head3 params + +A hash reference of alliance properties. None of which can contain profanity or restricted characters. You can update any or all of these properties at the same time. + +=over + +=item forum_uri + +The URI to a forum where alliance discussion occurs. + +=item description + +Information that is publcily available about an alliance. + +=item announcements + +Information that is only available to alliance members. + +=back + + + + =cut \ No newline at end of file diff --git a/docs/Inbox.pod b/docs/Inbox.pod index b812d48c..9451ea42 100644 --- a/docs/Inbox.pod +++ b/docs/Inbox.pod @@ -80,6 +80,8 @@ Exactly the same as C except that it shows archived messages instead Exactly the same as C except that it shows sent messages instead. + + =head2 read_message ( session_id, message_id ) Retrieves a message. Marks it read if it hasn't been already. @@ -141,6 +143,8 @@ A session id. A message_id that was returned using C or one of the other view_ methods. + + =head2 archive_messages ( session_id, message_ids ) Archives a list of messages. Returns: @@ -162,6 +166,9 @@ A session id. An array reference of message ids. + + + =head2 send_message ( session_id, recipients, subject, body, [ options ] ) Sends a message to other players. @@ -174,7 +181,9 @@ A session id. =head3 recipients -A comma separated string of empire names. B You may want to expose a search option here using L. +A comma separated string of empire names. In addition to an empire name, C<@ally> is available as a shortcut to send to all members of an alliance. + +B You may want to expose a search option here using L. =head3 subject diff --git a/lib/Lacuna/DB/Result/Alliance.pm b/lib/Lacuna/DB/Result/Alliance.pm new file mode 100644 index 00000000..626798db --- /dev/null +++ b/lib/Lacuna/DB/Result/Alliance.pm @@ -0,0 +1,152 @@ +package Lacuna::DB::Result::Alliance; + +use Moose; +extends 'Lacuna::DB::Result'; +use Lacuna::Util qw(format_date); +use DateTime; + +__PACKAGE__->table('alliance'); +__PACKAGE__->add_columns( + name => { data_type => 'varchar', size => 30, is_nullable => 0 }, + leader_id => { data_type => 'int', size => 11, is_nullable => 1 }, + forum_uri => { data_type => 'varchar', size => 255, is_nullable => 0 }, + description => { data_type => 'text', is_nullable => 1 }, + announcements => { data_type => 'text', is_nullable => 1 }, + date_created => { data_type => 'datetime', is_nullable => 0, set_on_create => 1 }, +); + +__PACKAGE__->belongs_to('leader', 'Lacuna::DB::Result::Empire', 'leader_id', { on_delete => 'set null' }); +__PACKAGE__->has_many('members', 'Lacuna::DB::Result::Empire', 'alliance_id'); +__PACKAGE__->has_many('invites', 'Lacuna::DB::Result::AllianceInvite', 'alliance_id'); + + +sub date_created_formatted { + my ($self) = @_; + return format_date($self->date_created); +} + +sub get_status { + my $self = shift; + my $members = $self->members; + my @members_list; + while (my $member = $members->next) { + push @members_list, { + empire_id => $member->id, + name => $member->name, + }; + } + return { + id => $self->id, + members => \@members_list, + leader_id => $self->leader_id, + forum_uri => $self->forum_uri, + description => $self->description, + announcements => $self->announcements, + date_created => $self->date_created_formatted, + }; +} + +sub get_invites { + my $self = shift; + my $invites = $self->invites; + my @out; + while (my $invite = $invites->next) { + my $empire = $invite->empire; + push @out, { + id => $invite->id, + name => $empire->name, + empire_id => $empire->id, + }; + } + return \@out; +} + + +sub send_invite { + my ($self, $empire, $message) = @_; + Lacuna::Verify->new(content=>\$message, throws=>[1005,'Message must not contain restricted characters or profanity.', 'message']) + ->no_tags + ->no_profanity; + if (! defined $empire ) { + confess [1002, 'No empire specified.']; + } + elsif ($empire->alliance_id == $self->id) { + confess [1010, 'Already a member of this alliance.'] + } + my $invite = Lacuna->db->resultset('Lacuna::DB::Result::AllianceInvite')->new({ + alliance_id => $self->id, + empire_id => $empire->id, + })->insert; + $empire->send_predefined_message( + from => $self->leader, + tags => ['Correspondence'], + filename => 'alliance_invite.txt', + params => [$self->name, $message], + ); + return $invite; +} + +sub withdraw_invite { + my ($self, $invite, $message) = @_; + Lacuna::Verify->new(content=>\$message, throws=>[1005,'Message must not contain restricted characters or profanity.', 'message']) + ->no_tags + ->no_profanity; + if (! defined $invite ) { + confess [1002, 'No invite specified.']; + } + my $empire = $invite->empire; + $invite->delete; + $empire->send_predefined_message( + from => $self->leader, + tags => ['Correspondence'], + filename => 'alliance_withdraw_invite.txt', + params => [$self->name, $message], + ); +} + +sub add_member { + my ($self, $empire) = @_; + if (! defined $empire ) { + confess [1002, 'No empire specified.']; + } + elsif ($empire->alliance_id && $empire->alliance_id != $self->id) { + confess [1010, 'Already a member of another alliance.'] + } + elsif ($empire->alliance_id == $self->id) { + confess [1010, 'Already a member of this alliance.'] + } + $empire->alliance_id($self->id); + $empire->update; + Lacuna->db->resultset('Lacuna::DB::Result::Probes')->search({empire_id => $empire->id})->update({alliance_id => $self->id}); + Lacuna->db->resultset('Lacuna::DB::Result::AllianceInvite')->search({empire_id => $empire->id})->delete; + return $self; +} + +sub remove_member { + my ($self, $empire, $delete_leader) = @_; + if (! defined $empire ) { + confess [1002, 'No empire specified.']; + } + elsif ($empire->alliance_id != $self->id) { + confess [1010, 'Not a member of this alliance.']; + } + elsif ($empire->id == $self->leader_id && !$delete_leader) { + confess [1010, 'The leader of the alliance cannot be removed from the alliance.']; + } + $empire->alliance_id(undef); + $empire->update; + Lacuna->db->resultset('Lacuna::DB::Result::Probes')->search({empire_id => $empire->id})->update({alliance_id => undef}); + return $self; +} + +before delete => sub { + my $self = shift; + my $members = $self->members; + while (my $member = $members->next) { + $self->remove_member($member, 1); + } +}; + + +no Moose; +__PACKAGE__->meta->make_immutable(inline_constructor => 0); diff --git a/lib/Lacuna/DB/Result/AllianceInvite.pm b/lib/Lacuna/DB/Result/AllianceInvite.pm new file mode 100644 index 00000000..546db8c2 --- /dev/null +++ b/lib/Lacuna/DB/Result/AllianceInvite.pm @@ -0,0 +1,25 @@ +package Lacuna::DB::Result::AllianceInvite; + +use Moose; +extends 'Lacuna::DB::Result'; +use Lacuna::Util qw(format_date); +use DateTime; + +__PACKAGE__->table('alliance_invite'); +__PACKAGE__->add_columns( + alliance_id => { data_type => 'int', is_nullable => 0 }, + empire_id => { data_type => 'int', is_nullable => 0 }, + date_created => { data_type => 'datetime', is_nullable => 0, set_on_create => 1 }, +); + +__PACKAGE__->belongs_to('empire', 'Lacuna::DB::Result::Empire', 'empire_id'); +__PACKAGE__->belongs_to('alliance', 'Lacuna::DB::Result::Alliance', 'alliance_id'); + +sub date_created_formatted { + my ($self) = @_; + return format_date($self->date_created); +} + + +no Moose; +__PACKAGE__->meta->make_immutable(inline_constructor => 0); diff --git a/lib/Lacuna/DB/Result/Building/Embassy.pm b/lib/Lacuna/DB/Result/Building/Embassy.pm index 611e4ce4..4e3b6bd0 100644 --- a/lib/Lacuna/DB/Result/Building/Embassy.pm +++ b/lib/Lacuna/DB/Result/Building/Embassy.pm @@ -41,5 +41,188 @@ use constant water_consumption => 6; use constant waste_production => 1; +sub max_members { + my $self = shift; + return $self->level * 2; +} + +sub alliance { + my $self = shift; + my $empire = $self->body->empire; + unless ($empire->alliance_id) { + confess [1002, 'You are not part of any alliance.']; + } + return $empire->alliance; +} + +sub create_alliance { + my ($self, $name) = @_; + my $empire = $self->body->empire; + if ($empire->alliance_id) { + confess [1010, 'Cannot form a new alliance, while you belong to an existing alliance.']; + } + my $alliances = Lacuna->db->resultset('Lacuna::DB::Result::Alliance'); + Lacuna::Verify->new(content=>\$name, throws=>[1000,'Alliance name not available.', 'name']) + ->length_lt(31) + ->length_gt(2) + ->not_empty + ->no_padding + ->no_restricted_chars + ->no_profanity + ->ok( !$alliances->search({name=>$name})->count ); + my $alliance = $alliances->new({ + name => $name, + leader_id => $empire->id, + }); + $alliance->insert; + $alliance->add_member($empire); + return $alliance; +} + +sub get_alliance_status { + my $self = shift; + return $self->alliance->get_status; +} + +sub accept_invite { + my ($self, $invite, $message) = @_; + Lacuna::Verify->new(content=>\$message, throws=>[1005,'Message must not contain restricted characters or profanity.', 'message']) + ->no_tags + ->no_profanity; + if ($invite->empire_id ne $self->body->empire_id) { + confess [1010, 'You cannot accept an invite that is not yours.']; + } + my $empire = $self->body->empire; + if ($empire->alliance_id) { + confess [1010, 'You are already a member of an alliance. Leave that one before joining another.']; + } + my $alliance = $invite->alliance; + $alliance->add_member($empire); + $invite->alliance->leader->send_predefined_message( + from => $empire, + tags => ['Correspondence'], + filename => 'alliance_reject.txt', + params => [$message, $empire->name], + ); +} + +sub reject_invite { + my ($self, $invite, $message) = @_; + Lacuna::Verify->new(content=>\$message, throws=>[1005,'Message must not contain restricted characters or profanity.', 'message']) + ->no_tags + ->no_profanity; + if ($invite->empire_id ne $self->body->empire_id) { + confess [1010, 'That invite is not yours to reject.']; + } + my $empire = $self->body->empire; + $invite->alliance->leader->send_predefined_message( + from => $empire, + tags => ['Correspondence'], + filename => 'alliance_reject.txt', + params => [$message, $empire->name], + ); + $invite->delete; +} + +sub leave_alliance { + my ($self, $message) = @_; + Lacuna::Verify->new(content=>\$message, throws=>[1005,'Message must not contain restricted characters or profanity.', 'message']) + ->no_tags + ->no_profanity; + my $empire = $self->body->empire; + unless ($empire->alliance_id) { + confess [1010, 'You cannot leave an alliance you are not part of.']; + } + my $alliance = $empire->alliance; + $alliance->remove_member($empire); + $alliance->leader->send_predefined_message( + from => $empire, + tags => ['Correspondence'], + filename => 'alliance_leave.txt', + params => [$message, $empire->name], + ); +} + +sub send_invite { + my ($self, $empire, $message) = @_; + my $alliance = $self->alliance; + my $count = $alliance->members->count; + $count += $alliance->invites->count; + if ($count >= $self->max_members ) { + confess [1009, 'You may only have '.$self->max_members.' in or invited to this alliance.']; + } + $alliance->send_invite($empire, $message); +} + +sub withdraw_invite { + my ($self, $invite, $message) = @_; + $self->alliance->withdraw_invite($invite, $message); +} + +sub dissolve_alliance { + my ($self) = @_; + my $alliance = $self->alliance; + if ($self->body->empire_id != $alliance->leader_id) { + confess [1010, 'Only the alliance leader can dissolve an alliance.']; + } + $alliance->delete; +} + +sub assign_alliance_leader { + my ($self, $empire) = @_; + my $alliance = $self->alliance; + if ($self->body->empire_id != $alliance->leader_id) { + confess [1010, 'Only the alliance leader can set another alliance leader.']; + } + if ($empire->alliance_id != $alliance->id) { + confess [1010, 'Cannot set a non-member to lead an alliance.']; + } + $alliance->leader_id($empire->id); + $alliance->update; +} + +sub get_pending_invites { + my $self = shift; + my $alliance = $self->alliance; + if ($self->body->empire_id != $alliance->leader_id) { + confess [1010, 'Only the alliance leader can view pending invites.']; + } + return $alliance->get_invites; +} + +sub get_my_invites { + my $self = shift; + my $invites = Lacuna->db->resultset('Lacuna::DB::Result::AllianceInvites')->search({empire_id => $self->body->empire_id}); + my @out; + while (my $invite = $invites->next) { + my $alliance = $invite->alliance; + push @out, { + id => $invite->id, + name => $alliance->name, + alliance_id => $alliance->id, + }; + } + return \@out; +} + +sub update_alliance { + my ($self, $params) = @_; + my $alliance = $self->alliance; + if ($self->body->empire_id != $alliance->leader_id) { + confess [1010, 'Only the alliance leader can update the alliance information.']; + } + my %vetted; + foreach my $field (qw(forum_uri description announcements)) { + my $content = $params->{$field}; + Lacuna::Verify->new(content=>\$content, throws=>[1005,'The '.$field.' must not contain restricted characters or profanity.', $field]) + ->no_tags + ->no_profanity; + $vetted{$field} = $content; + } + $alliance->update(\%vetted); + return $alliance; +} + + no Moose; __PACKAGE__->meta->make_immutable(inline_constructor => 0); diff --git a/lib/Lacuna/DB/Result/Empire.pm b/lib/Lacuna/DB/Result/Empire.pm index 26dfe781..a49a66b7 100644 --- a/lib/Lacuna/DB/Result/Empire.pm +++ b/lib/Lacuna/DB/Result/Empire.pm @@ -43,6 +43,7 @@ __PACKAGE__->add_columns( happiness_boost => { data_type => 'datetime', is_nullable => 0, set_on_create => 1 }, facebook_uid => { data_type => 'bigint', is_nullable => 1 }, facebook_token => { data_type => 'varchar', size => 100, is_nullable => 1 }, + alliance_id => { data_type => 'int', is_nullable => 1 }, ); @@ -54,6 +55,7 @@ sub sqlt_deploy_hook { } +__PACKAGE__->belongs_to('alliance', 'Lacuna::DB::Result::Alliance', 'alliance_id', { on_delete => 'set null' }); __PACKAGE__->belongs_to('species', 'Lacuna::DB::Result::Species', 'species_id', { on_delete => 'set null' }); __PACKAGE__->belongs_to('home_planet', 'Lacuna::DB::Result::Map::Body', 'home_planet_id'); __PACKAGE__->has_many('planets', 'Lacuna::DB::Result::Map::Body', 'empire_id'); @@ -369,6 +371,7 @@ sub add_probe { empire_id => $self->id, star_id => $star_id, body_id => $body_id, + alliance_id => $self->alliance_id, })->insert; # send notifications @@ -411,11 +414,15 @@ has probed_stars => ( lazy => 1, default => sub { my $self = shift; - my $probes = $self->probes; - my @stars; - while ( my $probe = $probes->next ) { - push @stars, $probe->star_id; + my %search = ( + empire_id => $self->id, + ); + if ($self->alliance_id) { + %search = ( + alliance_id => $self->alliance_id, + ); } + my @stars = Lacuna->db->resultset('Lacuna::DB::Result::Probes')->search(\%search)->get_column('id')->all; return \@stars; }, ); @@ -425,16 +432,23 @@ has count_probed_stars => ( lazy => 1, default => sub { my $self = shift; - return Lacuna->db->resultset('Lacuna::DB::Result::Probes')->search({empire_id=>$self->id})->count; + return $self->probes->count; }, ); before 'delete' => sub { my ($self) = @_; + $self->probes->delete; + Lacuna->db->resultset('Lacuna::DB::Result::AllianceInvite')->search({empire_id => $self->id})->delete; + if ($self->alliance_id) { + my $alliance = $self->alliance; + if ($alliance->leader_id == $self->id) { + $alliance->delete; + } + } $self->sent_messages->delete; $self->received_messages->delete; $self->medals->delete; - $self->probes->delete; my $planets = $self->planets; while ( my $planet = $planets->next ) { $planet->sanitize; diff --git a/lib/Lacuna/DB/Result/Map/Body/Planet.pm b/lib/Lacuna/DB/Result/Map/Body/Planet.pm index acfda2dc..8580ac1a 100644 --- a/lib/Lacuna/DB/Result/Map/Body/Planet.pm +++ b/lib/Lacuna/DB/Result/Map/Body/Planet.pm @@ -119,34 +119,39 @@ around 'get_status' => sub { $out->{empire} = { name => $self->empire->name, id => $self->empire_id, - alignment => 'hostile' + alignment => 'hostile', }; - } - if (defined $empire && $empire->id eq $self->empire_id) { - if ($self->needs_recalc) { - $self->tick; # in case what we just did is going to change our stats + if (defined $empire) { + if ($empire->id eq $self->empire_id) { + if ($self->needs_recalc) { + $self->tick; # in case what we just did is going to change our stats + } + $out->{needs_surface_refresh} = $self->needs_surface_refresh; + $out->{empire}{alignment} = 'self'; + $out->{building_count} = $self->building_count; + $out->{population} = $self->population; + $out->{water_capacity} = $self->water_capacity; + $out->{water_stored} = $self->water_stored; + $out->{water_hour} = $self->water_hour; + $out->{energy_capacity} = $self->energy_capacity; + $out->{energy_stored} = $self->energy_stored; + $out->{energy_hour} = $self->energy_hour; + $out->{food_capacity} = $self->food_capacity; + $out->{food_stored} = $self->food_stored; + $out->{food_hour} = $self->food_hour; + $out->{ore_capacity} = $self->ore_capacity; + $out->{ore_stored} = $self->ore_stored; + $out->{ore_hour} = $self->ore_hour; + $out->{waste_capacity} = $self->waste_capacity; + $out->{waste_stored} = $self->waste_stored; + $out->{waste_hour} = $self->waste_hour; + $out->{happiness} = $self->happiness; + $out->{happiness_hour} = $self->happiness_hour; + } + elsif ($self->empire->alliance_id == $empire->alliance_id) { + $out->{empire}{alignment} = 'ally'; + } } - $out->{needs_surface_refresh} = $self->needs_surface_refresh; - $out->{empire}{alignment} = 'self'; - $out->{building_count} = $self->building_count; - $out->{population} = $self->population; - $out->{water_capacity} = $self->water_capacity; - $out->{water_stored} = $self->water_stored; - $out->{water_hour} = $self->water_hour; - $out->{energy_capacity} = $self->energy_capacity; - $out->{energy_stored} = $self->energy_stored; - $out->{energy_hour} = $self->energy_hour; - $out->{food_capacity} = $self->food_capacity; - $out->{food_stored} = $self->food_stored; - $out->{food_hour} = $self->food_hour; - $out->{ore_capacity} = $self->ore_capacity; - $out->{ore_stored} = $self->ore_stored; - $out->{ore_hour} = $self->ore_hour; - $out->{waste_capacity} = $self->waste_capacity; - $out->{waste_stored} = $self->waste_stored; - $out->{waste_hour} = $self->waste_hour; - $out->{happiness} = $self->happiness; - $out->{happiness_hour} = $self->happiness_hour; } return $out; }; diff --git a/lib/Lacuna/DB/Result/Probes.pm b/lib/Lacuna/DB/Result/Probes.pm index 573cca05..bc437fc6 100644 --- a/lib/Lacuna/DB/Result/Probes.pm +++ b/lib/Lacuna/DB/Result/Probes.pm @@ -7,14 +7,16 @@ use DateTime; __PACKAGE__->table('probes'); __PACKAGE__->add_columns( - empire_id => { data_type => 'int', size => 11, is_nullable => 0 }, - star_id => { data_type => 'int', size => 11, is_nullable => 0 }, - body_id => { data_type => 'int', size => 11, is_nullable => 0 }, + empire_id => { data_type => 'int', is_nullable => 0 }, + star_id => { data_type => 'int', is_nullable => 0 }, + body_id => { data_type => 'int', is_nullable => 0 }, + alliance_id => { data_type => 'int', is_nullable => 1 }, ); __PACKAGE__->belongs_to('empire', 'Lacuna::DB::Result::Empire', 'empire_id'); __PACKAGE__->belongs_to('star', 'Lacuna::DB::Result::Map::Star', 'star_id'); __PACKAGE__->belongs_to('body', 'Lacuna::DB::Result::Map::Body', 'body_id'); +__PACKAGE__->belongs_to('alliance', 'Lacuna::DB::Result::Alliance', 'alliance_id'); no Moose; diff --git a/lib/Lacuna/RPC/Building/Embassy.pm b/lib/Lacuna/RPC/Building/Embassy.pm index 0d6fcfaa..6e7247b5 100644 --- a/lib/Lacuna/RPC/Building/Embassy.pm +++ b/lib/Lacuna/RPC/Building/Embassy.pm @@ -11,6 +11,167 @@ sub model_class { return 'Lacuna::DB::Result::Building::Embassy'; } +sub assign_alliance_leader { + my ($self, $session_id, $building_id, $empire_id, $message) = @_; + my $empire = $self->get_empire_by_session($session_id); + my $building = $self->get_building($empire, $building_id); + unless ($empire_id) { + confess [1002, 'You must specify which empire you want to take over leadership.']; + } + my $new_leader = Lacuna->db->resultset('Lacuna::DB::Result::Empire')->find($empire_id); + unless (defined $new_leader) { + confess [1002, 'The empire you specified to take over as leader does not exist.']; + } + $building->assign_alliance_leader($new_leader); + return { + status => $self->format_status($empire, $building->body), + alliance => $building->alliance, + }; +} + +sub create_alliance { + my ($self, $session_id, $building_id, $name) = @_; + my $empire = $self->get_empire_by_session($session_id); + my $building = $self->get_building($empire, $building_id); + my $alliance = $building->create_alliance($name); + return { + status => $self->format_status($empire, $building->body), + alliance => $alliance->get_status, + }; +} + +sub get_alliance_status { + my ($self, $session_id, $building_id) = @_; + my $empire = $self->get_empire_by_session($session_id); + my $building = $self->get_building($empire, $building_id); + return { + status => $self->format_status($empire, $building->body), + alliance => $building->get_alliance_status, + }; +} + +sub dissolve_alliance { + my ($self, $session_id, $building_id) = @_; + my $empire = $self->get_empire_by_session($session_id); + my $building = $self->get_building($empire, $building_id); + $building->dissolve_alliance; + return { + status => $self->format_status($empire, $building->body), + }; +} + +sub leave_alliance { + my ($self, $session_id, $building_id, $message) = @_; + my $empire = $self->get_empire_by_session($session_id); + my $building = $self->get_building($empire, $building_id); + $building->leave_alliance($message); + return { + status => $self->format_status($empire, $building->body), + }; +} + +sub accept_invite { + my ($self, $session_id, $building_id, $invite_id, $message) = @_; + my $empire = $self->get_empire_by_session($session_id); + my $building = $self->get_building($empire, $building_id); + unless ($invite_id) { + confess [1002, 'You must specify an invite id.']; + } + my $invite = Lacuna->db->resultset('Lacuna::DB::Result::AllianceInvite')->find($invite_id); + unless (defined $invite) { + confess [1002, 'Invitation not found.']; + } + $building->accept_invite($invite, $message); + return $self->get_alliance_status($empire, $building); +} + +sub reject_invite { + my ($self, $session_id, $building_id, $invite_id, $message) = @_; + my $empire = $self->get_empire_by_session($session_id); + my $building = $self->get_building($empire, $building_id); + unless ($invite_id) { + confess [1002, 'You must specify an invite id.']; + } + my $invite = Lacuna->db->resultset('Lacuna::DB::Result::AllianceInvite')->find($invite_id); + unless (defined $invite) { + confess [1002, 'Invitation not found.']; + } + $building->reject_invite($invite, $message); + return { + status => $self->format_status($empire, $building->body), + }; +} + +sub withdraw_invite { + my ($self, $session_id, $building_id, $invite_id, $message) = @_; + my $empire = $self->get_empire_by_session($session_id); + my $building = $self->get_building($empire, $building_id); + unless ($invite_id) { + confess [1002, 'You must specify an invite id.']; + } + my $invite = Lacuna->db->resultset('Lacuna::DB::Result::AllianceInvite')->find($invite_id); + unless (defined $invite) { + confess [1002, 'Invitation not found.']; + } + $building->withdraw_invite($invite, $message); + return { + status => $self->format_status($empire, $building->body), + }; +} + +sub send_invite { + my ($self, $session_id, $building_id, $empire_id, $message) = @_; + my $empire = $self->get_empire_by_session($session_id); + my $building = $self->get_building($empire, $building_id); + unless ($empire_id) { + confess [1002, 'You must specify which empire you want to invite.']; + } + my $invitee = Lacuna->db->resultset('Lacuna::DB::Result::Empire')->find($empire_id); + unless (defined $invitee) { + confess [1002, 'The empire you specified to invite does not exist.']; + } + $building->send_invite($invitee, $message); + return { + status => $self->format_status($empire, $building->body), + }; +} + +sub get_pending_invites { + my ($self, $session_id, $building_id) = @_; + my $empire = $self->get_empire_by_session($session_id); + my $building = $self->get_building($empire, $building_id); + return { + invites => $building->get_pending_invites, + status => $self->format_status($empire, $building->body), + }; +} + +sub get_my_invites { + my ($self, $session_id, $building_id) = @_; + my $empire = $self->get_empire_by_session($session_id); + my $building = $self->get_building($empire, $building_id); + return { + invites => $building->get_my_invites, + status => $self->format_status($empire, $building->body), + }; +} + + +sub update_alliance { + my ($self, $session_id, $building_id, $params) = @_; + my $empire = $self->get_empire_by_session($session_id); + my $building = $self->get_building($empire, $building_id); + my $alliance = $building->update_alliance($params); + return { + alliance => $alliance->get_status, + status => $self->format_status($empire, $building->body), + }; +} + + +__PACKAGE__->register_rpc_method_names(qw(update_alliance get_pending_invites get_my_invites assign_alliance_leader create_alliance dissolve_alliance send_invite accept_invite withdraw_invite reject_invite leave_alliance get_alliance_status)); + + no Moose; __PACKAGE__->meta->make_immutable; diff --git a/lib/Lacuna/RPC/Building/Observatory.pm b/lib/Lacuna/RPC/Building/Observatory.pm index c8a4662a..87bbe9be 100644 --- a/lib/Lacuna/RPC/Building/Observatory.pm +++ b/lib/Lacuna/RPC/Building/Observatory.pm @@ -41,7 +41,7 @@ sub get_probed_stars { my @stars; $page_number ||= 1; my $probes = Lacuna->db->resultset('Lacuna::DB::Result::Probes')->search( - { empire_id => $empire->id, body_id => $building->body_id }, + { body_id => $building->body_id }, { rows => 25, page => $page_number } ); while (my $probe = $probes->next) { diff --git a/lib/Lacuna/RPC/Inbox.pm b/lib/Lacuna/RPC/Inbox.pm index ca632681..9c5cbf35 100644 --- a/lib/Lacuna/RPC/Inbox.pm +++ b/lib/Lacuna/RPC/Inbox.pm @@ -87,14 +87,29 @@ sub send_message { my $send_count = $cache->get($cache_key,$empire->id); foreach my $name (split /\s*,\s*/, $recipients) { next if $name eq ''; - my $user = Lacuna->db->resultset('Lacuna::DB::Result::Empire')->search({name => $name})->next; - if (defined $user) { - push @sent, $user->name; - push @to, $user; - $send_count++; + if ($name eq '@ally') { + if ($empire->alliance_id) { + my $allies = $empire->alliance->members; + while (my $ally = $allies->next) { + push @sent, $ally->name; + push @to, $ally; + $send_count++; + } + } + else { + push @unknown, '@ally'; + } } else { - push @unknown, $name; + my $user = Lacuna->db->resultset('Lacuna::DB::Result::Empire')->search({name => $name},{rows => 1})->single; + if (defined $user) { + push @sent, $user->name; + push @to, $user; + $send_count++; + } + else { + push @unknown, $name; + } } } if ($send_count > 100) { diff --git a/t/Embassy.t b/t/Embassy.t new file mode 100644 index 00000000..ee936ff7 --- /dev/null +++ b/t/Embassy.t @@ -0,0 +1,61 @@ +use lib '../lib'; +use Test::More tests => 13; +use Test::Deep; +use Data::Dumper; +use 5.010; +use DateTime; + +use TestHelper; +my $tester = TestHelper->new->generate_test_empire->build_infrastructure; +my $session_id = $tester->session->id; +my $empire = $tester->empire; +my $home = $empire->home_planet; + +my $result; + +$result = $tester->post('embassy', 'build', [$session_id, $home->id, 0, 3]); +ok($result->{result}{building}{id}, "built an embassy"); +my $embassy = $tester->get_building($result->{result}{building}{id}); +$embassy->finish_upgrade; + +$result = $tester->post('embassy', 'create_alliance', [$session_id, $embassy->id, 'test alliance']); +ok(exists $result->{result}, 'can create alliance'); + +$result = $tester->post('inbox','send_message', [$session_id, '@ally', 'my subject', "test"]); +is($result->{result}{message}{sent}[0], $tester->empire_name, 'can send allies a message'); + +$result = $tester->post('embassy', 'get_alliance_status', [$session_id, $embassy->id]); +ok(exists $result->{result}, 'can get alliance'); + +$result = $tester->post('embassy', 'update_alliance', [$session_id, $embassy->id, { forum_uri => 'http://www.google.com' }]); +ok(exists $result->{result}, 'can update alliance'); + +$result = $tester->post('embassy', 'send_invite', [$session_id, $embassy->id, 1]); +ok(exists $result->{result}, 'invite the lacunans'); + +$result = $tester->post('embassy', 'get_pending_invites', [$session_id, $embassy->id]); +ok(exists $result->{result}, 'get invitations'); + +$result = $tester->post('embassy', 'withdraw_invite', [$session_id, $embassy->id, $result->{result}{invites}[0]{id}]); +ok(exists $result->{result}, 'uninvite the lacunans'); + +$result = $tester->post('embassy', 'reject_invite', [$session_id, $embassy->id, 9999]); +is($result->{error}{code}, 1002, 'cannot reject a non-existant invite'); + +$result = $tester->post('embassy', 'accept_invite', [$session_id, $embassy->id, 9999]); +is($result->{error}{code}, 1002, 'cannot accept a non-existant invite'); + +$result = $tester->post('embassy', 'assign_alliance_leader', [$session_id, $embassy->id, 1]); +is($result->{error}{code}, 1010, 'cannot set non member as alliance leader'); + +$result = $tester->post('embassy', 'leave_alliance', [$session_id, $embassy->id]); +is($result->{error}{code}, 1010, 'cannot leave an alliance if you are the leader'); + +$result = $tester->post('embassy', 'dissolve_alliance', [$session_id, $embassy->id]); +ok(exists $result->{result}, 'dissolve alliance'); + + + +END { + $tester->cleanup; +} diff --git a/t/Inbox.t b/t/Inbox.t index 25178a4c..67100b33 100644 --- a/t/Inbox.t +++ b/t/Inbox.t @@ -19,11 +19,11 @@ $result = $tester->post('inbox','view_inbox', [$session_id, { tags=>['Tutorial'] is(scalar(@{$result->{result}{messages}}), 1, 'fetching by tag works'); $result = $tester->post('inbox','view_inbox', [$session_id]); -is($result->{result}{message_count}, 5, 'message_count works'); +is($result->{result}{message_count}, 6, 'message_count works'); ok($result->{result}{messages}[0]{subject}, 'view inbox works'); my $message_id = $result->{result}{messages}[0]{id}; $result = $tester->post('empire','get_status', [$session_id]); -is($result->{result}{empire}{has_new_messages}, 5, 'new message count works'); +is($result->{result}{empire}{has_new_messages}, 6, 'new message count works'); $result = $tester->post('inbox', 'read_message', [$session_id, $message_id]); is($result->{result}{message}{id}, $message_id, 'can view a message'); diff --git a/var/messages/alliance_accept.txt b/var/messages/alliance_accept.txt new file mode 100644 index 00000000..008fdeb6 --- /dev/null +++ b/var/messages/alliance_accept.txt @@ -0,0 +1,7 @@ +Invitation Accepted +~~~ +I accept your invitation. + +%s + +%s diff --git a/var/messages/alliance_invite.txt b/var/messages/alliance_invite.txt new file mode 100644 index 00000000..22a41858 --- /dev/null +++ b/var/messages/alliance_invite.txt @@ -0,0 +1,5 @@ +Invitation +~~~ +We would like to formally invite you to join %s. + +%s diff --git a/var/messages/alliance_leave.txt b/var/messages/alliance_leave.txt new file mode 100644 index 00000000..65fc65f1 --- /dev/null +++ b/var/messages/alliance_leave.txt @@ -0,0 +1,7 @@ +Leaving Alliance +~~~ +I am leaving the alliance. + +%s + +%s diff --git a/var/messages/alliance_reject.txt b/var/messages/alliance_reject.txt new file mode 100644 index 00000000..9ac8dac6 --- /dev/null +++ b/var/messages/alliance_reject.txt @@ -0,0 +1,7 @@ +Invitation Rejected +~~~ +I'm not interested in joining your alliance at this time. + +%s + +%s diff --git a/var/messages/alliance_withdraw_invite.txt b/var/messages/alliance_withdraw_invite.txt new file mode 100644 index 00000000..7305fa70 --- /dev/null +++ b/var/messages/alliance_withdraw_invite.txt @@ -0,0 +1,5 @@ +Invitation Withdrawn +~~~ +We have decided to withdraw the invitation for you to join %s. + +%s -- 2.51.2